Action Templates are declarative data constructs that reduce the amount of work to create new Agroups actions. They are considered the extensibility feature of Agroups. All Agroups predefined and user defined actions are implemented with Action Templates. Hopefully the real power of action templates is realized in user defined templates. This chapter describes how a user can create new action templates and hence add to the user collection of general actions.
To create new action templates you should know something about Elisp. It is possible to know a little about Elisp and learn as you go. It will be helpful if you actually try the examples in this chapter to see the effect and get some facility with both the form of templates and some Elisp.
In some of the sections that follow, for precision we use dot notation to describe various forms. So as not to cause confusion for the new Lisp user we just remind that the dotted list (x . (a b c)) is equivalent to the list (x a b c).
In this section we show an extremely simple and probably useless action template just to spark interest and get us started. This will be an action template that has one slot: some text to display as a message in the minibuffer. Here is this simple template that creates a new action called simple-message
(agroups-add-action-template '((action simple-message) (id "Simple Message") (keys "sm") (afun (message text)) (slots (text "Text of message"))))
agroups-add-action-template is used to create new
actions and is given a single argument being a template. The template
is a declarative data structure represented as a list of properties. In
this simple example the properties are:
slots. We will go through these one
by one to see what they mean in the next section. But first to get a
more overall picture of what this does, evaluate the above
agroups-add-action-template. After evaluating this template see
if it has been added by entering
KA KO v A
and note that is a capital A. You should be able to see this registered in the user action collection in the Agroups buffer
User defined actions Simple Message (keys: s m)
At this point for all practical purposes this now looks like any of the given actions in the Predefined Action collection. Furthermore we should now be able to create an entry of this action in the current group. So lets do this with
KA KO A s m
and again note that this is a capital A. When Agroups prompts for the "Text of message" slot enter
Text of message ---- Edit or enter below and type C-cC-c when done ---- Hello World
but don't put a RET after the text "Hello World". Then type
C-cC-c to enter the slot value and give it whatever id and keys
you want. Now execute the action and you should see
in the minibuffer. You are now a creator of new Agroups actions!
As promised in the previous section Simple Action Template Example we will now go through each of the properties in the template we created. Just to recall our simple action template
(agroups-add-action-template '((action simple-message) (id "Simple Message") (keys "sm") (afun (message text)) (slots (text "Text of message"))))
Actually the only property that is required is
action and all
others are optional and serve to make more and more complex actions.
For example you could create an action with nothing more than
(agroups-add-action-template '((action not-much)))
Of course if you have the time to experiment you will find that the action is well named, ie. what it does is not-much!
action property makes the action distinct from any other and
is needed behind the scenes to indicate an action of this type. This
action symbol will appear in the Agroups save file when the
simple-message action entry was created. If you were to look you would
see that it appears as u-simple-message instead of simple-message.
Agroups given actions are guaranteed to never begin with "u-" which
stands for User defined. Agroups adds this prefix to every user defined
template action and does the book keeping to maintain this so that the
user never has to worry about creating an action that clashes with any
id property is a short one line identifying description of
the action. We say "short one line" since this will appear in the user
defined actions list that you saw above. We say "identifying" because
the id is the string that completion will work on if the user chooses to
use completion instead of the accelerator keys you specify. And the
keys property is the accelerator keys you specify.
afun property is the action function that gets applied to the
slot variables. So in the simple-message action that we defined we had
(afun (message text))
The value of the
afun property is more or less what you would
expect with an ordinary Elisp function call on variables. In this
example the function
message is applied to the variable
text. We say "more or less what you would expect" because
instead of just allowing variables as arguments you can have any kind of
list structure or structures where the slot variables get substituted on
entry creation. For example if the
message function allowed a
list of text strings instead of just a text string, we could have
(afun (message (text)))
Note that the list
(text) is not quoted. No part of any nested
list structures have to be quoted since nothing in such lists get
evaluated. Just the variables are substituted for values.
The substitution of the variable values brings us to the
property. The whole purpose of the slots property is to provide a list
of variables that will be bound to the values determined by a specific
entry instance of the action template. So our simple-message action
template we have
(slots (text "Text of message"))
and this defined the variable
text that on action entry creation
gets a value that is bound to the variable
text in the above
message form. In our further example of a user
creating a simple-message entry the
text slot variable got bound
to the string value "Hello World".
That completes the simple understanding of action templates. The rest of the capabilities of action templates are achieved through more of these action templates properties and slot properties. They expand on those additional properties. But first, in the next section we give a formal but brief description of the full syntax the top level action template form and in subsequent sections we give the full syntax and semantics of each template property form.
We saw in the previous section that the function
takes an action template as an argument and adds that in the form of an action to the user defined collection of actions. This template argument itself is quoted so that the template can be passed as a variable. Even though we quote the top level template list structure, none of the nested lists or symbols need to be quoted. An exception would be if the template designer explicitly wants a quote for some reason.
The action template itself is simply a list of properties of the form
(<property 1> <property 2> ... <property n>)
Each property is a list of the form
(<property name> <value 1> <value 2> ... <value n>)
where <property name> is a Template Property symbol listed below and the <value n> are the specific values that make this template distinct from other templates. When we use just the <property name> to refer to the property in this document it is meant to connote the entire property above with property name and values.
The only required property is the
action property all others are
optional and have defaults. The following table lists all template
properties and their defaults on the right:
Template Property Default --------------------------------------------------------- * action No default, required * id An id would be constructed from action * keys No keys, user would have to use completion * afun No afun, action entry will not execute * slots No data for template, all entries the same pfun No preprocessing before template expansion exists No existing condition check default-slot Default slot when user hits RET is first slot
Although the only property required is the
action property all
good action templates should have the properties marked with * above.
The syntax and semantics of each of these is described in the following
The action template property is a list of the form
Where <action> should be a symbol (or list see below) unique from other user action symbols unless the enclosing user template is intended to replace another existing template with the same action symbol. In the context of the action template we refer to the action symbol as simply "the action". The action is what distinguishes one action from another in the collection of all Agroups actions.
When the template is added to the system, Agroups changes this symbol to u-<action>. The reason for this is so that the user does to not have to worry about clashes with Agroups given actions. Agroups given action symbols are guaranteed not to begin with "u-".
It is also possible for <action> to be a list of action symbols and in this case all rules above apply to each symbol in this list. For example the Agroups given "File or dired" action is specified in its template as
(action (file dired))
When such an action template is added with a list of actions it must
pfun property to set the action, See section pfun Template Property. Usually this is done by the
pfun function based on
the current environment and most likely the current buffer. For
example, the Agroups given "File or dired" action sets the action of the
user created entry based on whether the buffer is a dired or a buffer
associated with a file. If no
pfun property is specified or a
pfun function does not set the action then Agroups will just
assume the first symbol of the list is the action.
The id template property is a list of the form
(id <descriptive string>)
where <descriptive string> is a short string designed to fit on one line describing the action. There should be no line terminators in this string. If it is more than one line Agroups will just take the first line of the string without the terminators. It should be descriptive but relatively short since it appears in action lists and edit buffers. This is also the string that will be used by the Agroups user when using completion on an action id.
The term "afun" means action function. The action function is the
function that brings about the final result of executing an action
afun template property has the form
(afun <function spec>)
Where <function spec> is what you would normally expect with a function call. So the form of <function spec> is
(<function> . <s-expression list>)
where <function> is any function symbol or lambda. Anywhere in the <s-expression list> a slots property slot symbol may appear which results in that symbol being substituted for the slot values when the action is expended into an entry. There is no evaluation either before or after the slot variable substitutions. Then only the substituted s-expressions in the <s-expression list> along with the action symbol are placed in the user's newly created entry instance. When that entry instance is executed the <function> is looked up based on the action in the entry instance and applied to the instance s-expressions.
The no evaluation before or after clause above indicates that nothing in
the <s-expression list> needs to be quoted. However there are
cases where the template designer might want to include quotes. An
example is where the <function> is
eval and the <s-expression
list> is just a single s-expression containing a necessarily quoted
To illustrate implications of all of the above we give two action templates that would have the same action entry execution effect but would create different entries in the Agroups save file. Then we make several specific points about these action templates that follow from the above general description.
The action template
(agroups-add-action-template '((action compose-message) (afun (message "%s %s" first second)) (slots (first "First part of message") (second "Second part of message"))))
would have the same action entry execution effect as
(defun myfunction (first second) (message "%s %s" (car first) second)) (agroups-add-action-template '((action compose-message) (afun (myfunction (first) second)) (slots (first "First part of message") (second "Second part of message"))))
Lets say for the sake of illustration that the user creates two entries from these action templates and in both cases enters the date "Hello" for the first slot and "World" for the second slot. Then both of those entries when executed will result in the message "Hello World" being displayed in the minibuffer. It might help to actually try adding these two templates and then two corresponding action entries to see what gets generated in the action entries themselves that you can see in the save file with "KA KO v s". Now for the specific points:
afun s-expressions can be any s-expression]
In the second version of the compose-message action the
(afun (myfunction (first) second))
is kind of silly because all that the myfunction does is take the car of the first argument which appears in the entry as the list ("Hello"). The following would make more sense in this case
(afun (myfunction1 first second))
but the more awkward myfunction version helps visualize the idea of
substituting the slot variable
first in the <s-expression list>
[point 2: The
afun function is any function symbol or lambda]
The first version of the compose-message action used a standard existing
message and the second version uses a user defined
myfunction. Also note that we can use a lamda instead
(agroups-add-action-template '((action compose-message) (afun ((lambda (first second) (message "%s %s" (car first) second)) (first) second)) (slots (first "First part of message") (second "Second part of message"))))
[point 3: no evaluation either before or after the slot variable substitutions]
In the version
(afun (myfunction (first) second))
the resulting user entry appears as
(... compose-message ("Hello") "World")
which shows that neither (first) nor ("Hello") was evaluated, just the
first was substituted with the value "Hello" and
second was substituted with the value "World".
[point 4: only the substituted s-expressions in the <s-expression list> along with the action symbol are placed in the user's newly created entry instance]
Generally speaking it is better to use an <s-expression list> that is as
least complicated as possible. So
<s-expression list> is better than the
<s-expression list> which is better than the message <s-expression
list>. You can see this by the lists appearing in each user entry
instance for each function version:
message version: (... compose-message "%s %s" "Hello" "World") myfunction version: (... compose-message ("Hello") "World") myfunction1 version: (... compose-message "Hello" "World")
The myfunction1 version not only has the least amount of data in the user's entry collection but it is easier to change or add to the effects of the action later without having the user to recreate his entries derived from the action. In fact the message version is horrible because the "%s %s" string would occur in every user's entry needlessly.
[point 5 <function> is looked up based on the action in the entry instance and applied to the instance s-expressions]
You can see from the entry instances listed in point 4 that nowhere does the actual action function appear an the entry instance. However note that the s-expression lists are quite different. This means that without the user having to change his entry instances of your action you can change the function but you can't change the topology of the s-expression lists. This is yet another reason in support point 4 that the less complex list of s-expressions is better.
The slots template property is the heart of the template since the variety of values of the slots will allow one general action template to provide an infinite variety of specific action entry instances to the Agroups user.
The form of the slots property is a list of the form
(slots . <slots list>)
Where <slots list> is a list of zero or more individual slot forms. You
can actually specify no slots but that would not be a very useful
template since every action entry generated from such a template of the
same action would do the same thing. The section in this document on
afun property describes how slots are actually used when an
action entry is executed, See section afun Template Property.
The subsections below expand on the individual slot form. The Form of a Template Slot subsection describes the general syntax and semantics of a slot form and subsequent subsections describe the specific slot properties.
An individual template slot is a list of the form
(<slot symbol> <slot id> . <slot properties>)
The <slot symbol> is a symbol that should appear in the
form, See section afun Template Property. A specific value will be
substituted for the <slot symbol> in the
afun form when the
template is expanded to create an entry.
The <slot id> is either a short but descriptive single string or if more description is necessary a list of strings. This string or strings should not have any kind of line terminators. They will automatically be interpreted as one line or multiple lines. The <slot id> will appear to the user when creating or editing an action entry slot.
The <slot properties> is a list of zero or more slot properties that we will describe in subsequent subsections. If a slot form specifies zero slot properties then Agroups defaults will be chosen. The following table lists all slot properties with their defaults on the right:
Slot Property Default ------------------------------------------------------------- type Slot data type will be of type
stringselect Slot data will not be selected by user but entered sfun No slot data function, user will be asked for data editor Default Agroups slot editor printer Default Agroups slot data printer based on type
In the following subsections that have specific descriptions of these slot properties each property applies to the enclosing slot form that we defined at the beginning of this section. In particular it applies to how the slot data is acquired and manipulated.
type template slot property will indicate the Elisp data type
of the expected value of the slot data. The type template slot property
has the form
(type <Elisp type symbol or t>)
Where <Elisp type symbol or t> is an unquoted symbol. Elisp data types
are permissible such as
number ... etc. with the
addition of the Agroups special type
t which means any Elisp data
type. The default if no type slot property is specified is
The type property is used by the default Agroups editor and printer so
that it knows how to edit or present the data. Also the
function is guaranteed to receive the type of data that is specified for
a slot or
string if not specified.
select template slot property allows the template designer to
prompt the user for some number of fixed values with descriptions when
creating or editing the slot. When the user chooses a value it becomes
the slot value. It has the form
(select . <list of select choices>)
where <list of select choices> is a list select choices of the form
(<choice description> <value>)
The <choice description> is a string or list of strings exactly like the <slot id> above and the same rules apply. The value must be the type specified by the type slot property or if type property is not specified a string. Here is an example
(agroups-add-action-template '((action select-message) (afun (message text)) (slots (text "Text of message" (select ("Message says Hello" "Hello") ("Message says Goodbye" "Goodbye"))))))
Note that no type is specified for the text slot. Since the default is
string the select data "Hello" and "Goodbye" are not in
error since they are strings. If instead we had the symbols
goodbye as the select values in the above
(select ("Message says Hello" hello) ("Message says Goodbye" goodbye))
Then when expanding that template Agroups would give us, the template designers, an error
Error: Template slot: text is not of type: string, it has value: hello
We could fix this by adding a (type symbol) property but then the user
would get an error when trying to execute an entry generated from our
template since the function
message takes a string and not a
symbol and it would then complain. So here is a similar example that
uses symbols as values but uses
eval as an action function with
some code that uses the selected select symbol
(agroups-add-action-template '((action select-message) (afun (eval (message (symbol-name 'sym)))) (slots (sym "Text of message" (type symbol) (select ("Message says Hello" hello) ("Message says Goodbye" goodbye))))))
Once again however this is a poor action since what it would put in the user's entry is too complex and specific. Template designer written functions are usually better for processing select values like numbers, symbols lists etc.. So in the above case
(afun (somefunction sym))
would be much better where
somefunction processes the selected
value of sym in some way and the user's entry for example would be
(... select-message hello)
which is far less complex and easier to deal with in the future than
(... select-message (message (symbol-name (quote hello))))
sfun template slot property which stands for Slot Value
Function will return a value that becomes the value of a slot based on
the environment at action entry creation time. This overrides any other
method for getting the slot value, such as a
select template slot
property. However the
sfun property is ignored when editing the
slot value. It has the form
(sfun <slot value function>)
where <slot value function> is a function symbol or lambda. For example the following
(defun buffer-region () (buffer-substring (region-beginning) (region-end))) (agroups-add-action-template '((action region-message) (afun (message text)) (slots (text "Text of message" (sfun buffer-region)))))
sets the text slot of a new action entry to the current buffer's region.
But then when the user edits such an entry the
sfun would be
ignored and the existing text slot would be place in the standard
Agroups edit buffer for editing.
editor template slot property is used when the standard
Agroups editor is not suitable for a slot and a special editor would be
better. It has the form
(editor <editor function>)
where <editor function> is a function symbol or lambda representing the
function that Agroups will pass as the single argument to <editor
function> the value of the slot to edit. This function must return the
edited value. For example the Agroups given keyboard macro action
template stores the keyboard macro string in a slot called
that uses the Standard Emacs keyboard macro editor
(macro "Keyboard macro" (editor <kbd-macro editor function>))
Agroups prefix arguments can be passed to this editor using the Agroups argument variables, See section Accessing Agroups Prefix Arguments. In fact the Agroups given keyboard macro action does this and in the above the <kbd-macro editor function> is a function that calls the Emacs keyboard macro editor and passed the Agroups prefix argument to it.
Normally Agroups presents the current value of a slot in the standard
Agroups editor by printing the value in the Agroups buffer according to
the type of the slot data. For example if the slot type is
string it just prints the string verbatim, but if the type is
t then it pretty prints the expected Elisp.
printer template slot property allows this behavior to be
overridden by the template designers printer. It has the form
(printer <print function>)
where <print function> is a function symbol or lambda that prints the slot data and takes as an argument the slot data.
There are two ways that Agroups prints a template slot value in the standard Agroups editor. It prints the slot value in an Agroups buffer when displaying the whole entry object to be edited and also when editing the individual slot it places the existing slot value in the Agroups edit buffer by printing it. Usually these get printed the same way. But occasionally those two get printed in different ways. For example the Keys Macro action prints the keys in a the whole entry object view as standard Emacs keys display form. But this form is not suitable for editing the actually slot keys, the standard display form has embedded spaces for example.
The default printer or if specified for the slot the
template slot property will be used for both ways unless the
edit-printer template slot property is specified. When this
property is specified it will be used for printing into the edit
buffer of the individual slot where the user will actually edit it.
It has the form
(edit-printer <print function>)
where <print function> is a function symbol or lambda that prints the slot data and takes as an argument the slot data.
When the user completes a slot edit by typing
Agroups edit buffer is parsed for the entered data based on the
default or specified type of the slot. If the
template slot property is specified for the slot then the parsed
resulting object will be passed to the
This slot property has the form
(edit-receiver <receiver function>)
where <receiver function> is the receiver function that takes one
argument of the parsed object and returns the object that will be
stored and associated with the slot in the user's save file. Usually
this works in conjunction with the
edit-printer template slot
property whose function converts the stored representation back to the
form that the receiver function gets before the printer function
pfun template property specifies a preprocessing function
that gets called before template slot expansion takes place. This
function can set action attributes and slot values before template
expansion. The common use for this is to collect information about the
current environment, like the current buffer, and set that information
as the entry data when an entry is created.
pfun property has the form
(pfun <preprocessing function>)
where <preprocessing function> is a function symbol or lambda. This function is run before any slot processing is done and it can optionally set the template action and any of the slots using the functions
The agroups-set-entry-attribute function call has the form
(agroups-set-entry-attribute <entry attribute> <value>)
where <entry attribute> is one of 'action or 'entry-id and <value> is a symbol in the case of 'action and a string in the case of 'entry-id.
Use 'action when you want to preset the action of an entry. This is almost exclusively used when the template action property specifies a list of two or more actions. For example the "Buffer associated File or dired" action has template that specifies two actions
(action (file dired))
Then in the
pfun function that it specifies it sets the action
depending on whether the current buffer has a file associated with it or
a dired. If the template was specified with multiple actions and does
not specify a
pfun that sets one of these actions then Agroups
will just use the first action in the list of actions when an action
entry is generated.
Use 'entry-id when you want to preset the default id of an entry.
Normally when the user is prompted for an entry id and just hits RET an
Agroups default id is constructed from the action symbol, but if the
entry-id attribute is set by the
pfun then that will be used
instead of the Agroups default.
The agroups-set-entry-slot function call has the form
(agroups-set-entry-slot <slot symbol> <slot value>)
where <slot symbol> is a quoted symbol matching one of the slot symbols
of one of the slots of the template and <value> is an Elisp value of the
type specified in that template slot. The template designer can preset
a slot value using a
pfun in this way or an
only difference is that it may be more convenient to use the
method to set a slot value in circumstances where the action is also
computed by the
pfun or a number of slots share the same
The exists template property is used to trigger something when an one or more entries exist in the current group with the template action that specifies the exists property and some existing environment condition exists. It is typically used when creating a new action entry and some entry already exists of the same action and value of one or more template slots. When a template specifies the exists property and such a condition exists the user is given the option of triggering something that the exists property specifies. If the user accepts and then there are more than one such existing entry then the user is given a choice of those entries.
The exists template property has the form
(exists <exists predicate> <exists trigger>)
The <exists predicate> is a function call form that returns non-nil if the condition exists. The form of the <exists predicate> is
(<function> . <slot symbol list>)
where <function> is any function symbol or lambda and <slot symbol list> is zero or more <slot symbol> symbols that appear in the template slots property.
The <exists trigger> for the time being has only one form
(update . <slot symbol list>)
where <slot symbol list> is a list of one or more slot symbols in the template slots property. The idea is that when the <exists predicate> returns non-nil an update of the existing slots gets triggered. In other words instead of creating a new entry the chosen existing entry slots get updated based on the specified slot's value properties.
A good example of an action using the exists property is the given
Agroups "File Point" action. When that operation is executed by virtue
of its template action
exists property the current group is
checked if any actions of
point exists and if those actions have
the same file as the current buffer's file then the the user chooses to
update the existing entry or not. It does this with an exists property
(exists (<predicate> file) (update point))
where <predicate> is a function that returns non-nil if its argument is
the same file as the current buffer's file. The template has two slots
point being the point within the file of slot
then what the above exists property in effect says is: If the current
groups has a
point action entry then ask the user if he wants to
update that entry and if the user chooses then update the
slot. And since the template has for the slot
(point "Point to go to" (type integer) (sfun point))
sfun function is the standard Elisp function
which returns the current point in the buffer. Consequently the exiting
entry's point is updated to the current point in the current buffer.
Here is a not quite so good example of using the
slot property but is easier to understand and try
(agroups-add-action-template '((action file-message) (afun ((lambda (file) (message "File reminder: %s" file)) file)) (exists (buffer-file-name) (update file)) (slots (file "File to show in message" (sfun buffer-file-name)))))
When the Agroups editor prompts for a slot to edit it gives a default in parentheses so that if the user just hits RET it selects that slot to edit. Normally this default slot is the first slot in the slots property but the template designer can change this with the default-slot property.
The default-slot template property has the form
(default-slot <slot symbol>)
where <slot symbol> is the slot symbol whose choice will appear as the default slot when the Agroups editor asks for a slot.
Any function or lambda specified in an action template
sfun or any function subsequently invoked from
those can access two special variables to capture the prefix argument
given when executing an action entry of this action type. The variable
will contain the actual prefix argument. So, for example if the prefix argument was C-u this variable would contain (4). The variable
contains the standard Emacs numeric interpretation of agroups-arg. So, for example if the prefix argument was C-u this variable would contain 4. Or to be very precise its value is always
Here is an example of an
afun property function using agroups-arg
(agroups-add-action-template '((action compose-message) (afun ((lambda (first second) (message "%s %s" (if agroups-arg second first) (if agroups-arg first second))) first second)) (slots (first "First part of message") (second "Second part of message"))))
So if you create an entry of this action and try it it should display a message in the minibuffer with the first slot and then the second slot and then try it with a C-u prefix argument before KA it should display the message with the second slot first and then the first slot.
Go to the first, previous, next, last section, table of contents.