The options
package provides easy to use key-value options for
package writers. It has a similar interface as pgfkeys
with
path options but comes with more built-in data types and more convenient
support for families and searching.
The main features of options
are:
Declare your options only once: in most packages you usually need to
declare both a new command, and the option that sets it. In the
options
package you declare the option just once. For example,
\options{/my/len/.new length}
and then use it anywhere else as
\option{/my/len}
.
Use paths for keys: just like pgfkeys
, the options
package uses
paths (instead of families) to declare options and prevent name clashes
between different packages. Paths are convenient for complex options,
like border/left/width
, and are also convenient to specify searches.
Many built-in data types: the options
library comes with many
useful data types like choice
, list
, toggle
, num
, dim
,
length
, glue
, commands, and plain values, and it is easy to add
your own (Section 3.1. Also you can hook into existing
definitions like an if
or counter
(Section 3.3).
Value options: You can define value-only options that start with a
special character, like "Georgia"
for a font option, or
!8080FF
for a color option (Section 6.3).
Convenient searches: you can specify paths that should be searched from other paths and redirect even from absolute paths. For complex packages this is very useful to inherit common options (Section 2.2).
Easy filtering: it is easy to collect unknown options and process them later. Combined with the search mechanism this makes it easy to do custom processing (Section 2.2).
It is fast: for simple user options, the options
package is a bit
faster than pgfkeys
and if searches or filters are involved it is
usually about twice as fast as pgfkeys
(and about six times faster
as xkeyval
) (Section 8).
Handles class and package options: use the same option declarations to handle the options passed to a class or package (Section 5.1).
Defining options is very easy. As an example, we will make an \mybox
command
that takes named options. Our goal is that the user can invoke \mybox
like:
\mybox{padding=1ex, border/width=0.4pt, font/style=italic}{text}
We can define the options for our \mybox
command as:
\options{
/mybox/.new family,
/mybox/padding/.new length = \fboxsep,
/mybox/border/width/.new length = \fboxrule * 2,
/mybox/border/color/.new color,
/mybox/font/style/.new choice = {normal,italic,small-caps},
}
The options are all specified as a path where names that start with a
dot, like .new length
are called handlers. In this case, the handlers
create the options for our box command. To parse the options passed by
the user, we use the same \options
command:
\newcommand\mybox[2]{%
{\options{/mybox,#1}%
\myboxdisplay{#2}%
}%
}
Here we start the option list with /mybox
which makes that the default
path (because it was declared with .new family
) so the user can give
relative option names instead of prefixing all options with /mybox/
.
Options are always set local to the current group.
Finally, we can use \option{〈option〉}
to get the value of an option.
This command expands directly to the command name (i.e. \optk@〈option〉
)
and is very efficient. So, our implementation for displaying our box may
look like:
\newcommand\myboxdisplay[1]{%
\setlength\fboxsep{\option{/mybox/padding}}%
\setlength\fboxrule{\option{/mybox/border/width}}%
\colorlet{currentcolor}{.}%
\color{\option{/mybox/border/color}}%
\fbox{%
\color{currentcolor}%
\ifcase\option{/mybox/font/style/@ord}\relax
\or\itshape
\or\scshape
\fi
#1%
}%
}
Here, instead of using the font/style
directly, we use the automatically
generated font/style/@ord
that gives the ordinal of the choice on which we
can match more efficiently. Here is our new command in action:
Here is a \mybox{padding=1ex, border/color=blue!70, font/style=italic}{boxed} text.
There are many ways to refine our implementation, for example, we can use styles to make it easier to set multiple options at once:
\options{ /mybox/border/normal/.new style* =
{/mybox/border/width=0.4pt, /mybox/border/color=black }}
The *
after the style
signifies that this option does not expect
an argument, and we can use it as:
A \mybox{border/normal}{normal} border.
Another improvement is in our choice definition for the font. Instead of using a case over the choice ordinal, we can set the required font command directly as the value of the choice options, we show this in Section 3.1.
The option paths are mainly used to prevent name clashes between different
packages but they can also be used for searches. In particular, we can
specify for any path that if a sub-path cannot be found, we should look
under another path. Suppose we define a new \fancymybox
command that
takes some extra options. In that case, we would like to re-use the
/mybox
options and look for any undefined options under /mybox
instead:
\options{
/fancymybox/.new family = {/mybox}, % search also /mybox
/fancymybox/border/radius/.new length,
/fancymybox/rounded/.new style = [5pt]{/fancymybox/border/radius = #1},
}
Note how the rounded
style takes an argument which is defaulted to 5pt
.
In the .new family
declaration, we can provide a list of search paths: here we just
gave /mybox
such that any options not found under /fancymybox
will be looked up
under /mybox
:
\options{/fancymybox, rounded=10pt, border/normal}
In the previous sample, /mybox/border/normal
is invoked. In general, we
can add comma separated search paths either in a .new family
declaration
or using the .search also
handler. Search paths can be added to any path
and will apply recursively. For example, if we set:
\options{
/a/foo/x/.new cmd* = x,
/b/bar/y/.new cmd* = y,
/c/a/.search also = {/a/foo},
/c/b/.search also = {/b},
/c/.new family = {/a,/b},
}
Then all of the following options are resolved:
\options{ /c/foo/x, /c/a/x, /c/b/bar/y, /c/bar/y}
Note that the options
package will even search if an absolute path
is given, and always searches with the relative sub-path.
This is important for modularity since it allows us for example to combine
options of different sub packages. For example, if I want to handle options
under /package A
and /package B
together, I can just define a new
family:
\options{ /packageAB/.new family = {/package A,/package B}}
and start processing options using \options{/packageAB,..}
. Even
when the user now uses absolute paths like /packageAB/〈name〉
the
option search will route it automatically to the sub packages.
Also note that for efficiency, the basic \option
command does not
search and always expands directly to the command name. Therefore, in
implementation code we still need to use \option{/a/foo/x}
and cannot
use \option{/c/foo/x}
for example.
It is possible to handle only options under some path and ignore
any unknown options. For example, give our previous options, we can
only process the options under /c/a
as:
\options{/options/collectunknown, /c/a/.cd, x, bar/y }\\
\options{/options/remaining/.show}
Here we used the /options/collectunknown
style to signify that we
want to collect unknown options into the /options/remaining
list.
We used the .cd
handler to change the default path to /c/a
such
that only x
is found (as /a/foo/x
) but the bar/y
option is put
in the remaining list.
Any remaining options can be processed eventually using the \optionswithremaining
command:
\optionswithremaining{/c/b/.cd}
The command takes a list of options that are processed before the options
in the remaining list. It is allowed to pass in /options/collectunknown
right away again to collect any new remaining options.
The /options/remaining
is list is only cleared when using the /options/collectunknown
style. This can be useful to collect unknown options using multiple passes
of \optionsalso
.
Besides using the remaining list, you can also define general @unknown
handlers on any path. When an option not found, the library looks
bottom-up for @unknown
handlers and invokes it with the path and its
argument if found. The general handler /@unknown
will raise a package
error.
You can customize this behavior by installing an @unknown
handler
yourself:
\options{
/mybox/@unknown/.new cmd 2 =
{I don't know option ``#1'' (=#2).},
/mybox/silly = hi
}
As an aside, note that we needed to put braces around @unknown
handler
since it uses the =
character.
〈option〉/.new value
= [〈default〉]〈initial value〉
Defines a new 〈option〉
that with an 〈initial value〉
. A value option
just contains the value that was provided by the user. The 〈default〉
value is optional. If it is given, it is used when the user does not provide
an argument when using this option.
〈option〉/.new toggle
[= 〈bool〉]
Define a new toggle. These are boolean values and have the advantage
over the standard \newif
(see Section 3.3) that they only
require one macro instead of three. The initial value (if not given) is
false
and the default value is always true
. A toggle can be tested
using \iftoggle{〈toggle〉}
.
\options{/test/condition/.new toggle}
\options{/test/condition}% default sets to true
Toggle is \iftoggle{/test/condition}{true}{false}
Besides assigning a new value of true
or false
, you can
also flip a condition as:
\options{/test/condition/.flip}
〈option〉/.new choice
= [〈default〉]{〈choice1〉[=〈value1〉],…,〈choicen〉[=〈valuen〉]}
Defines a new choice option where the user can provide 〈choice1〉
to 〈choicen〉
as
arguments. The initial value is always 〈choice1〉
. We always need to enclose the
choice list with braces to distinguish the comma used to separate the choices from the
comma used to separate the options. For convenience, the new choice
handler also
defines 〈option〉/@ord
that contain the ordinal of the current choice (starting
at 0), and the 〈option〉/@name
that contains the current choice name.
The ordinal is a number and can be tested efficiently using
ifcase
and ifnum
. For example:
\options{/test/program/.new choice={latex,xelatex,luatex,pdftex}}
\options{/test/program = xelatex}
\noindent\options{/test/program/.show}
% case on ordinal
\ifcase\option{/test/program/@ord}%
latex
\or
xelatex
\else
other
\fi
% ifnum on ordinal
\ifnum\option{/test/program/@ord}<2\relax
latex or xelatex
\else
luatex or pdftex
\fi
Another powerful feature is to define the values that each choice
implies. By default this is the name of the choice but we can assign
anything else. For example, for our \mybox
command, we could have
specified the font style as:
\options{/test/font/style/.new choice=
{normal={}, italic=\itshape, small-caps=\scshape}}
The value of \option{/test/font/style}
is not the choice name now, but
the command that we assigned to it. We can now use the option directly
instead of doing a case on the @ord
value:
\options{/test/font/style=italic}
This is {\option{/test/font/style}in italics}.
〈option〉/.new list
[=[〈default〉]{〈elem1〉,…,〈elemn〉}]
Define a new comma separated list. The initial value if not given is the empty list. There are various operations to work with lists:
\letoptionlist{〈option〉}\〈macro〉
: stores the list in \〈macro〉
.
\optionlistdo{〈option〉}{〈cmd〉}
: invokes 〈cmd〉
on each element. The iteration
can be stopped by ending 〈cmd〉
with \listbreak
.
\ifoptioncontains{〈option〉}{〈elem〉}{〈true〉}{〈false〉}
: test if 〈elem〉
occurs
in the list and invokes either the 〈true〉
or 〈false〉
branch.
\options{/test/list/.new list = {banana,milk,eggs}}
\optionlistdo{/test/list}{%
Element ``\textsf{#1}''.
\ifstrequal{#1}{milk}{\listbreak}{}%
}
There are also two special handlers for manipulating lists in the options,
〈list option〉/.push = 〈elem〉
: pushes <elem
on the end of the list.
〈list option〉/.concat = {〈list〉}
: concatenates 〈list〉
to the end of the list.
For example,
\options{/test/list 2/.new list = {banana}}
\options{/test/list 2/.push = milk, /test/list 2/.show}
〈option〉/.new length
[= [〈default〉]〈dimexpr〉]
Defines a new length option 〈option〉
. This option stores its value in a
new length register, and its value be used directly where a length is
expected, e.g., \hspace{\option{〈option〉}}
. If no initial value is
given, the initial length is 0pt
. The user can assign
any length expressions (dimexpr), e.g., \options{〈option〉 = 1pt + \fboxsep}
.
〈option〉/.new dim
[= [〈default〉]〈dimexpr〉]
Defines a new dimension option 〈option〉
. This option stores its value
as an unevaluated dimexpr
, and its value be used directly where a
dimension is expected, e.g., \hspace{\option{〈option〉}}
. The main
difference with a length option is that a dimension option is not
evaluated at the time the key is assigned, but rather when it is used.
This may be important when relying on the contents of other registers.
For example, if we declare:
\setlength\fboxrule{1pt}
\options{/test/width/.new dim=\fboxrule,/test/length/.new length=\fboxrule}
\setlength\fboxrule{10pt}
This will show 10pt
for the width
, but 1pt
for the length:
\options{/test/width/.show}\\\options{/test/length/.show}
〈option〉/.new num
[= [〈default〉]〈numexpr〉]
Defines a new number option 〈option〉
. The assigned value
is evaluated as a numexpr. Can be used directly in any context
that expects a numexpr. For example:
\options{/test/num/.new num = 2+4}
Is it 6? \ifnum\option{/test/num}=6\relax Yes.\else No!\fi
〈option〉/.new glue
[=[〈default〉]〈glueexpr〉]
Defines a new glue option. Can be assigned any valid glue expression and used
in any context that expects a glue expression. If no initial value is given,
0pt
is used.
The options in this section are not values by themselves but are only invoked for their side effect.
〈option〉/.new family
[= 〈search list〉]
Defines a new family, this is a shorthand for
〈option〉/.search also = 〈search list〉,
〈option〉/.new style* = {〈option〉/.cd},
〈option〉/.type = family
〈option〉/.new style
= {〈options〉}
〈option〉/.new style*
= {〈options〉}
Defines a new style; when invoked it will also set the specified
〈options〉
using \optionsalso
. The .new style*
variant does not
take an argument itself. The plain .new style
can use #1
for the
argument value, e.g.
/border/width/.new style = {/border/top/width=#1,/border/bottom/width=#1}
〈option〉/.new cmd
= [〈default〉]〈code〉
〈option〉/.new cmd*
= 〈code〉
Define a new command that is invoked when the options is given.
.new cmd*
takes no argument while .new cmd
takes a single argument.
Use of these options is generally discouraged and if you can you should
try to use a data type directly together with \option{〈option〉}
commands.
\options{ /test/cmd/.new cmd = You said ``#1'',
/test/cmd = hi,
}
〈option〉/.new cmd 0
= 〈code〉
〈option〉/.new cmd 1
= [〈default〉]〈code〉
〈option〉/.new cmd 2
= [〈default〉]〈code〉
〈option〉/.new cmd 3
= [〈default〉]〈code〉
〈option〉/.new cmd 4
= [〈default〉]〈code〉
Define commands with multiple arguments, where .new cmd 0
is equal to .new cmd*
and .new cmd 1
to .new cmd
.
Each argument needs to be enclosed in braces and if not all
arguments are given, they will be empty.
\options{
/test/cmd2/.new cmd 2 = [{x}{y}]{I got (#1,#2)\\},
/test/.cd,
cmd2,
cmd2 = hi,
cmd2 = {hi},
cmd2 = {{hi}},
cmd2 = {hi}{there},
}
Note how {hi}
and hi
had the same effect since the options
processing
peels of a single layer of braces. If we want to preserve braces we need to double up.
〈option〉/.new cmd tuple
= 〈code〉
〈option〉/.new cmd triple
= 〈code〉
These are variants of .new cmd 2
and .new cmd 3
that take
exactly 2 or 3 arguments separated by commas.
〈option〉/.new cmdx
= {〈pattern〉}{〈postfix〉}{〈code〉}
This defines a plain command with the specified 〈pattern〉
.
Also, when invoking the command, it will append 〈postfix〉
to the argument which
is often necessary to ensure the command pattern will always match. For example,
here is how we defined .new cmd tuple
which matches with exactly 2 arguments:
/handlers/new cmd tuple/.new handler/.defaults = \optionsalso{
#1/.new cmdx = {##1,##2,##3}{,,}% match comma separated
{\ifstrequal{##3}{,}%
{#2}%
{\optionerror{#1}{expecting a 2 argument tuple}}%
},
#1/.type=cmd tuple,
}
Use these declarations if you need to work with existing definitions and are not able to declare your own using the data types in Section 3.1.
〈option〉/.is if
= 〈if name〉
Declare a new option that directly sets a defined if
declared with \newif
. The name should not start with the if
part, e.g.
\options{ /twocolumn/.newif = @twocolumn }
〈option〉/.is counter
= 〈counter name〉
Declare an option that directly sets or gets a counter
declared with \newcounter
. You can use .inc
, .step
, and .refstep
operations on counters.
〈option〉/.is def
= [〈default〉]〈macro name〉
Declare an option that directly sets or gets a defined definition.
This is basically equivalent to the \define@key
operation of the keyval
package.
\def\mytest{}
\options{
/mytest/.is def = \mytest,
/mytest = ``hi'',
}
\mytest{} there.
〈option〉/.is edef
= [〈default〉]〈macro name〉
Same as the .is def
but will use \edef
to redefine the
macro definition.
〈path〉/.cd
Change the directory to make 〈path〉
the default option prefix.
〈option〉/.show
Show the current option (in the document)
〈option〉/.typeout
Show the current option in the console.
〈option〉/.expand once
= 〈value〉
Expand the argument once (using \expandafter
)
〈option〉/.expand twice
= 〈value〉
Expand the argument twice.
〈option〉/.expanded
= 〈value〉
Fully expand the argument. Defined as:
/handlers/expanded/.new transformer =
\protected@edef\optionvalue{\optionvalue}
〈list option〉/.push
= 〈element〉
Push an element on the end a list option.
〈list option〉/.concat
= 〈list〉
Concatenate a list to the end of a list option.
〈option〉/.inc
[= 〈numexpr〉]
Increment a counter or number (.new num
) with the given amount (or 1 if
no argument is given)
〈counter option〉/.step
Step a counter option by 1.
〈counter option〉/.refstep
‘Ref step’ a counter option by 1.
/options/exec
= 〈code〉
Directly execute the provided 〈code〉
\options{ /options/exec=hi there}
/options/ignoreunknown
[= 〈bool〉]
If set to true, this will ignore any subsequent unknown options. Such
ignored options will be added to the /options/remaining
list.
/options/allowsearch
[= 〈bool〉]
If set to false
, it will no longer search along search paths
provided by .search also
or .new family
.
/options/unknownwarnonly
[= 〈bool〉]
If set to true, this only issues a warning when finding an unknown option (instead of raising an error).
/options/remaining
[= 〈options〉]
A list of remaining options to be processed. This list should
not be used directly but mostly in conjunction with /options/collectunknown
and \optionswithremaining
.
/options/collectunknown
A style. If given, it will clear the /options/remaining
list and
set /options/ignoreunknown
to true.
This section defines the command macros available on option values.
\options
{〈options〉}
Process the 〈options〉
list. This is a comma separated list of
〈option〉 [=〈value〉]
elements. Both the 〈option〉
and 〈value〉
are trimmed
of whitespace (at the end and start). The list may have empty elements and also
end or start with commas. When extracting the 〈value〉
a single layer of braces
is removed – this is done such that the user can specify values that contain
commas or equal signs themselves. Any option defines are always local to the
current group.
When invoking \options
the initial path is always the root (/
) and the
flag /option/ignoreunknown
is false, and /option/allowsearch
is true.
\optionsalso
{〈options〉}
Just like \options
except it will use the current default path and
option flags.
\optionswithremaining
{〈options〉}
Like \options
but also processes any options in the list /options/remaining
after processing 〈options〉
. Will start by making a copy of the /options/remaining
list so you can call /options/collectunknown
in 〈options〉
to immediately start
collecting further remaining options.
\options@ProcessOptions
{〈family〉}
Call this in a package (.sty
) or class (.cls
) file to handle the options
passed to it. In a package file it will also consider the known options that
are passed to the class besides the options passed to it directly. The
〈family〉
should be the root path for your options. For example,
\NeedsTeXFormat{LaTeX2e}[1995/12/01]
\ProvidesPackage{mypkg}[2015/01/01,By Me]
\RequirePackage{options}
\options{
/mypkg/.new family,
/mypkg/columns/.new num = [1]{2},
/mypkg/10pt/.new cmd* = \typeout{use 10pt font size},
}
\options@ProcessOptions{/mypkg}
Others can now pass options to your package as:
\usepackage[10pt,columns=2]{mypkg}
\option
{〈option〉}
This uses the current value of 〈option〉
. It directly expands to the
command \optk@〈option〉
and is very efficient. Usually that command contains
the value of the option, but sometimes it expands to something different depending
on the type of the options. For example, it may be a length register.
\letoption
{〈option〉}\〈macro〉
If you need to use an option in loop or need careful expansion control,
it is sometimes more efficient to \let
bind the option value into a
macro, e.g.
\letoption{/my/option}\myoptionvalue
\edefoption
{〈option〉}\〈macro〉
Same as \letoption
but fully expands the current value.
All these test take true and false branch as final arguments.
\ifoptiondefined
{〈option〉}{〈true〉}{〈false〉}
Is the option defined?
\ifoptionvoid
{〈option〉}{〈true〉}{〈false〉}
Is the option undefined or is its value blank (empty or spaces)?
\ifoptionblank
{〈option〉}{〈true〉}{〈false〉}
Is the option value blank (empty or spaces)?
\ifoptionequal
{〈option〉}{〈value〉}{〈true〉}{〈false〉}
Is the option value equal to the provided value?
\ifoptionanyof
{〈option〉}{〈list〉}{〈true〉}{〈false〉}
Does the option value occur in the comma separated value list 〈list〉
?
\ifoptiontype
{〈option〉}{〈type〉}{〈true〉}{〈false〉}
Does the option have type 〈type〉
?
\ifoptionnil
{〈list option〉}{〈true〉}{〈false〉}
Is list option value empty?
\ifoptioniscode
{〈option〉}{〈true〉}{〈false〉}
Is this option just an action? i.e. it has no associated value.
These are utility functions for list options.
\optionlistdo
{〈list option〉}{〈action〉}
Perform 〈action〉
on every element of the current value of 〈list option〉
.
The element is available as #1
in 〈action〉
and the iteration can be
stopped by ending with \listbreak
.
\letoptionlist
{〈list option〉}\〈macro〉
\let
bind the current list value as a list of comma separated values in \〈macro〉
.
This is sometimes needed since the internal representation of lists uses
another representation internally (in order to contain commas itself).
\ifoptioncontains
{〈list option〉}{〈true〉}{〈false〉}
Check if a list option contains a certain element.
\optionshow
{〈option〉}
Show the value of 〈option〉
.
Ignore unknown = \optionshow{/options/ignoreunknown}
\optionshowall
[〈bool〉]
Show all the options that are defined. If you pass true
in the optional
argument it also shows all the internal values under the /handlers/
which
can be a big list.
\optionshowpath
{〈option〉}
Show all the options defined under a certain path. Useful for debugging:
\optionshowpath{/mybox}
This section gives an overview of more advanced mechanisms, like defining handlers and new data types.
Names that start with a dot are handlers. These are commands that are
called with the current option path and argument, and are used for example to declare
new options (e.g. .new choice
), to change the environment (e.g. .cd
), or to
transform the argument (e.g. .expanded
).
You can define your own handler .〈name〉
by adding a command option under
/handlers/〈name〉
. For example, let's make a handler that transforms a
color argument from a HTML format #XXXXXX
to a named color:
\options{%
/handlers/htmlcolor/.new transformer = {%
% we need to change the \optionvalue
% #1 = current path, #2 = current \optionvalue
\definecolor{#1/@color}{HTML}{#2}%
\def\optionvalue{#1/@color}%
}
}
We can use our new handler directly with our previous box command:
A \mybox{border/color/.htmlcolor = 800080}{purple} box.
There are various kinds of handlers:
.new handler
: a general handler that does not chain nor invoke the original option.
.new operation
: chains with other handlers but does not invoke the original option.
.new transformer
: transforms the \optionvalue
and chains with other handlers.
Handlers are also used to define the standard data types and can be used
to define new data types yourself. Here is for example how the value
data type is defined:
\option{
/handlers/new value/.new handler = []\optionsalso{%
#1/.new cmd = \option@set{#1}{##1},
#1/.type = value,
#1/.initial = {#2},
},
}
The .new handler
receives the current path (#1
) and the initial value (#2
).
The default value provided at definition time is empty ([]
). When a user
defines a new value, we simply set more options based on the path. When the
defined option is set, the .new cmd
is called with the argument (##1
) and
we use that set the actual option value directly: \option@set{#1}{##1}
.
Usually, we do some additional processing here, for example, for choice values
we would check if the choice is valid.
Finally, we can also specify handlers that are invoked when the option name
starts with special character. This allows you to handle, say, a quoted value
like "Georgia"
as a shorthand for /font/family=Georgia
. As an example, we
will handle options that start with a bang (!
) as an HTML color.
Handling a special character 〈char〉
can be done simply by installing a handler
under /handlers/special/〈char〉
:
\options{%
/handlers/special/!/.new handler = {%
\optionsalso{ /mybox/border/color/.expanded/.htmlcolor = \@gobble#2 }%
},
}
A \mybox{!008080}{teal} box.
Here we used chaining to first expand the argument (.expanded
) and then
invoking the .htmlcolor
handler. We needed to use \@gobble
to remove
the exclamation mark from the provided argument (i.e. #2
will be
equal !008080
).
When \options
parses the options and finds an option and argument, it
will assign argument to \optionvalue
and search for the option:
/
)
then prepend the default path (\opt@defaultpath
).
〈option〉/@code
.
.〈name〉
look for the first
handler and invoke /handlers/〈name〉/@code
if it exists with
\option@handlerpath
set to the option path.
〈path〉/@searchalso
exists where
〈option〉
= 〈path〉/〈subpath〉
, then invoke the search also handler
with 〈subpath〉
as its argument, and keep searching recursively.
/@unknown
handlers and invoke
it the first one that is found. If none are defined, this will
invoke /@unknown
with 〈option〉
as its path which will raise
a package error.
This may seem like quite a bit of work but there has been much attention to efficiency. For example, we can match in a single definition for handlers since we evaluate chained handlers from left to right. Also, we never search for handlers but just match directly etc.
This section introduces option handlers and commands
that are useful when extending the options
package itself.
These operations transform an option itself. Usually only used when defining new handlers but should not be necessary in general.
〈option〉/.expands
When applied to an option it ensures the option argument is
always fully expanded (as if using .expanded
at every invocation).
〈option〉/.default
= 〈default value〉
Set the default argument if the user does not provide one.
The default argument is stored in 〈option〉/@def
.
〈option〉/.initial
= [〈default〉]〈initial〉
Set the default argument (if provided) and the initial value
of an option. The initial value is stored in 〈option〉/@ini
and used when invoking .reset
.
〈option〉/.reset
Sets the option back to its original value.
〈option〉/.undef
Undefine an option. This can be used to redefine existing options of other packages.
〈option〉/.search also
= 〈search paths〉
Specify additional search paths for the given 〈option〉
as
explained in Section 2.2.
〈option〉/.type
= 〈type〉
Specify the type of an option. This is used for example by the
default .show
function to display values correctly. The type
can be queried using \letoptiontype
, \ifoptiontype
, and
\ifoptioniscode
.
Some compatibility functions for pgfkeys
users.
〈option〉/.is family
Equivalent to .new family
(but without a search argument)
〈option〉/.code
= 〈code〉
Equivalent to .new cmd
.
〈option〉/.style
= 〈style〉
Equivalent to .new style
.
/handlers/〈name〉
= 〈code〉
Define such path to install a new handler 〈name〉
. The 〈code〉
takes two arguments (the current path and argument) and should be defined
using .new handler
, .new operation
, or .new transformer
.
/handlers/show/〈type〉
= 〈code〉
Provide custom show functions for options of type 〈type〉
. The 〈code〉
takes
a single argument, namely the option name. For example:
/handlers/show/length/.new cmd = \option@showvalue{\the\option{#1}}
Using \option@showvalue
ensures option values are shown consistently.
/handlers/special/〈char〉
= 〈code〉
Provide handlers for options that start with a special character, see Section 6.3.
\optionerror
{〈option〉}{〈message〉}
Raise a package error for 〈option〉
with a certain message.
\optionwarning
{〈option〉}{〈message〉}
Output a package warning for 〈option〉
with a certain message.
\optionerror@expect
{〈option〉}{〈expecting〉}
Raise a package error for 〈option〉
with an 〈expecting〉
message.
\letoptiontype
{〈option〉}\〈type〉
Bind the type of an option to \〈type〉
.
Properly takes care of command options.
\optionname
{〈option〉}
Expand to the internal name of an options, i.e. optk@〈option〉
.
\option@set
{〈option〉}{〈value〉}
Set the \optk@〈option〉
macro to 〈value〉
.
\option@eset
{〈option〉}{〈value〉}
Set the \optk@〈option〉
macro to a single expansion of 〈value〉
(using \expandafter
).
\option@def
{〈option〉}〈pattern〉{〈code〉}
Define the \optk@〈option〉
as a macro that takes parameters
defined in 〈pattern〉
.
\option@let
{〈option〉}\〈macro〉
\let
the \optk@〈option〉
macro to \〈macro〉
. This can be
more efficient than \options@set
if the value happens to be
a macro itself.
Internally, every option 〈option〉
comes with 〈option〉/@code
that
is invoked when an option is set with the \optionvalue
containing
the current argument. The \optionvalue
will equal \optionnovalue
when no argument is given. A single expansion of \optionvalue
will
yield the exact value provided by the user (minus one layer of outer braces).
The 〈option〉@code
usually starts with an argument check. If no argument
is expected (the *
options on commands) and error is raised if there was
an argument. If an argument is expected, and there is no argument, the
\optionvalue
is set to the default argument (in 〈option〉/@def
). If there
is no default, an error is raised.
\option@invoke
{〈option〉}{〈value〉}
\option@xinvoke
{〈option〉}{〈value〉}
\option@einvoke
{〈option〉}{〈value〉}
\option@invokedefault
{〈option〉}
Various ways to invoke a option handler directly. The \option@invoke
macros directly call 〈option〉/@code
. The einvoke
does a single
expansion of its argument, and xinvoke
does a full expansion. The
\option@invokedefault
call the 〈option〉/@code
with \optionvalue
set
to \optionnovalue
.
〈option〉/.new code
= [〈default〉]〈code〉
Define a new 〈option〉/@code
handler. This is invoked when the option
〈option〉
is set with the macro \optionvalue
set to the argument.
This will equal \optionnovalue
if no argument was provided. This is
the most primitive command handler and it is recommended to use
.new cmd
instead.
〈option〉/.new code*
= 〈code〉
Just like .new code
but checks that 〈option〉
did not get an argument,
i.e. that \optionvalue
equals \optionnovalue
.
To define handlers, we cannot use .new code
since the handler
needs access to the option path it is handling. The .new handler
gets also passed this path as an argument.
/handlers/〈name〉/.new handler
= [〈default〉]〈code〉
Define a new handler 〈name〉
. If 〈default〉
is provided,
this will be the default argument for the newly defined handler.
The 〈code〉
takes two arguments, the option path (#1
) when .〈name〉
is
invoked, and the provided argument (#2
). For example:
/handlers/new style*/.new handler = \optionsalso{
#1/.new cmd* = \optionsalso{#2},
#1/.type = style,
},
/handlers/〈name〉/.new handler*
= 〈code〉
Similar to .new handler
but for handlers that take on argument.
〈option〉/.new transformer
= [〈default〉]〈code〉
A form of handler that transforms a provided argument. Should redefine
\optionvalue
which will be passed to the next handler.
Takes the option path and current option argument as arguments.
For example:
/handlers/expanded/.new transformer=\protected@edef\optionvalue{\optionvalue}
〈option〉/.new operation
= [〈default〉]〈code〉
A form of handler that is invoked for its side effect. Takes the option path and current option argument as arguments. In contrast with a transformer after this handler, the original option will not be set and it only chains with other handlers. For example:
/handlers/concat/.new operation = \option@concat{#1}{#2}
〈option〉/.new operation*
= 〈code〉
Same as .new operation
but for operations that take no argument.
〈option〉/.new cmd transformer
= [〈default〉]〈code〉
A transformer that changes the option value of a command. For example:
/handlers/expands/.new cmd transformer={\protected@edef\optionvalue{\optionvalue}}
\optionprependcode
{〈option〉}{〈code〉}
Add some code to 〈option〉/@code
right after the argument check.
This is used for example by the .expands
handler to always
expand the \optionvalue
.
\optionnewcode
{〈option〉}{〈code〉}
Define a new 〈option〉/@code
handler that expects an argument.
\optionnewcode*
{〈option〉}{〈code〉}
Define a new 〈option〉/@code
handler that expects no argument.
\optionnewhandler
{〈option〉}{〈code〉}
Define a new 〈option〉/@code
handler for defining a handler that
will take an argument.
This will insert some code that calls 〈code〉
with the option
path that is handled in #1
and the argument in #2
.
\optionnewhandler*
{〈option〉}{〈code〉}
Define a new 〈option〉/@code
handler for defining a handler that
takes no argument.
This will insert some code that calls 〈code〉
with the option
path that is handled in #1
and the argument in #2
.
There are some performance numbers of the options
, pgfkeys
and
xkeyval
packages.
To test the performance, we performed 100.000 invocations of
\options
, pgfkeys
, and setkeys
respectively. For each library,
we defined two options in the family bar
and foo
, as:
\options{
/bar/bar-test/.new value = hi,
/foo/foo-test/.new value = world,
/foo/.new family={/bar},
}
We then tested two queries. The first one is simple and set an option that can be directly found:
\options{/foo,foo-test=a test}
and a complex one that needs a one-level search:
\options{/foo,bar-test=a test}
We measured the time of a run without any testing (the baseline) and then ran each benchmark 100.000 times and picked the best time out of three runs, subtracting the baseline time. The benchmarks were run on an Intel Core2 Quad CPU @ 3ghz with 4Gb memory running XeLaTeX 3.1415926 from TeX Live 2013/W32TeX. The results were:
simple | |||
---|---|---|---|
package | relative | total | 100.000 reps. |
options | 1.0x | 3.41s | 25ms per 1000 |
pgfkeys | 1.3x slower | 4.34s | 34ms per 1000 |
xkeyval | 8.1x slower | 27.77s | 268ms per 1000 |
complex (one level search) | |||
---|---|---|---|
package | relative | total | 100.000 reps. |
options | 1.0x | 5.32s | 44ms per 1000 |
pgfkeys | 2.1x slower | 11.10s | 101ms per 1000 |
xkeyval | 5.7x slower | 30.34s | 294ms per 1000 |
So both options
and pgfkeys
are quite a bit faster than xkeyval
,
and options
performs quite well when searches are involved. We also
tested against the basic keyval
package but this is a bit tricky since
keyval
does not support features like searching in the first place.
It was about 1.5 times faster on simple queries though.