If you are an Emacs user, you will not be surprised to learn that there is an Emacs mode for editing CFEngine configuration files. In this Appendix you will learn how to set up and use the CFEngine editing mode in Emacs.
You need GNU Emacs 23.1 or higher. Earlier versions or XEmacs may work as well, but have not been extensively tested.
First, you need to download cfengine.el from https://raw.github.com/cfengine/core/master/contrib/cfengine.el. Emacs currently includes an older version of this file. Emacs 24.2 and higher, when released, will include the latest version, but 24.1 and earlier do not, so you need to manually download it.
Assuming you have downloaded the library to your computer, you can add it to your load path. Follow the generic instructions from the Emacs wiki, or just put the following code in your .emacs file:
(autoload
'cfengine-mode"cfengine"
"cfengine editing"
t) (add-to-list
'load-path"/directory/where/the/library/lives/"
)
You will probably want to tell Emacs to associate .cf files with the cfengine-mode
, although that could cause
problems if you use other files ending with .cf. See http://www.emacswiki.org/emacs/AutoModeAlist for more
information or just add the following line to your .emacs file:
(add-to-list
'auto-mode-alist'
("\\.cf\\'"
.
cfengine-mode
))
If you use other files ending with .cf and would prefer not to associate this
extension with cfengine-mode
by
default, just run M-x
cfengine-mode
after you open the file and it will switch to the
CFEngine mode.
The Emacs Lisp function cfengine-mode
is actually a wrapper that tries
to figure out if you are editing CFEngine 3 or CFEngine 2 configurations.
If you open a blank file, it will assume CFEngine 2, but the next time it
has some text to examine, it will do the right thing. To eliminate the
uncertainty, replace cfengine-mode
with
cfengine3-mode
in the previous section.
It does the exact same thing except it always assumes CFEngine 3
syntax.
After you open a file with cfengine-mode
, in the Emacs modeline (the
summary status bar under the content) you will see CFE2
or CFE3
.
That tells you unambiguously which one is the active mode.
You can now edit the file. Hurrah! Type your masterpiece. The CFEngine mode offers some very useful commands. The following list summarize those I use a lot, but the Emacs command set contains a lot more that can apply. Each command is shown with both its default keybinding and the name of the function that invokes it.
M-h means hitting either the Alt or Meta key, depending on your keyboard, together with “h,” or hitting the ESC key followed by the “h” key. C-M-h means the same thing, but holding down the Ctrl key as well.
mark-paragraph
)Select a “paragraph” of text (delimited by empty lines).
mark-defun
)Select the whole bundle or body surrounding the cursor.
beginning-defun
)Move to the beginning of the current bundle or body, or to the previous one if the cursor is already at the beginning of it.
end-of-defun
)Move to the end of the current bundle or body, or the next one if the cursor is already at the end of it.
indent-for-tab-command
)Indent the current line. The exact indentation depends on how
you have set your cfengine-parameters-indent
variable, which is explained in the next section.
Once you are in cfengine-mode
,
syntax highlighting will be done according to CFEngine syntax, and
indentation will be done according to the parameters described in the
following section.
There are many other editing commands available in cfengine-mode
that are inherited from the Emacs
general text editing facilities. Explore Emacs and you will be able to
edit not just CFEngine configurations, but other kinds of text and code
easily and efficiently.
General information about Emacs can be found in its copious Info files, and in Learning GNU Emacs, by Debra Cameron, James Elliott, Marc Loy, Eric S. Raymond, and Bill Rosenblatt (O’Reilly).
You need to customize only two parameters to control indentation in
cfengine-mode
. Do this by typing
M-x customize-variable
and then typing the parameter name (you can use TAB completion so you
don’t have to type it out).
After you change any variable, make sure you save your changes in your ~/.emacs or in comments in your CFEngine configuration file, or the changes will take effect only until the end of your current Emacs session.
cfengine-indent
The size in spaces of one “indentation step.” Default value is 2.
The indentation step is the basic unit of measure for indenting different parts of a CFEngine policy:
The start of a bundle
or body
declaration is
indented zero steps (starts at the first column).
A promise type section (e.g. vars:
or files:
) is indented one step.
A class selector line (e.g. cfengine::
) is indented two
steps.
A promise is indented three steps. Indentation within the
promise is done according to the settings of
cfengine-parameters-indent
, described
later.
Attributes inside body
declarations are indented as if they were promise attributes,
according to the rules set by
cfengine-parameters-indent
.
Note that these indentation steps remain even when one of the elements before is not present. This means that a promise will always be indented three steps, even when it is not preceded by a class selector line. The idea is that all the elements of the same type are indented consistently throughout the policy file.
Indentation of CFEngine 3 promise parameters and of attributes
in body
declarations.
This is the screen you will see when you choose to customize this variable:
Cfengine Parameters Indent: Choice: [Value Menu]Anchor at beginning of promise
Choice: [Value Menu]Indent parameter name
Indentation amount from anchor:0
To understand what the different parts of the parameter mean, consider this code as reference:
bundle
x
y
{section
:class
::"promiser"
promiseparameter
=>
value
; }
In the first choice, you select whether promiseparameter
will be anchored “at the
beginning of the line” (absolutely) or “at the beginning of the
promise” (relative to "promiser"
). In the second choice, you
select whether you want to “indent the parameter name” (the start of
the word promiseparameter
will be
indented to a certain position) or to “indent the arrow” (this means
that the position of the arrow that separates the parameter and its
value will be calculated, and the rest of the line will be oriented
around it). Finally, you can choose the amount of the indentation,
in spaces.
The default is to anchor at promise, indent parameter name,
and offset by 0 characters, which results in this (observe that,
according to these parameters, the promise attributes, comment
and perms
, start at the same column as the
promiser "/tmp/netrc"
):
bundle
agent
rcfiles
{files
:any
::"/tmp/netrc"
comment
=>
"my netrc"
,perms
=>
mog
("600"
,"tzz"
,"tzz"
); }
If we change the offset to 2, we get this (promise attributes are indented two spaces with respect to the promiser):
bundle
agent
rcfiles
{files
:any
::"/tmp/netrc"
comment
=>
"my netrc"
,perms
=>
mog
("600"
,"tzz"
,"tzz"
); }
If we choose “anchor at the beginning of line,” “indent the arrow,” and offset by 10, we get this (the arrows in the parameters start at column 10 counted from the beginning of the line):
bundle
agent
rcfiles
{files
:any
::"/tmp/netrc"
comment
=>
"my netrc"
,perms
=>
mog
("600"
,"tzz"
,"tzz"
); }
Some coders like to “anchor at promise,” “indent arrow,” and offset 16 (in this case, the arrows start 16 columns after the promiser):
bundle
agent
rcfiles
{files
:any
::"/tmp/netrc"
comment
=>
"my netrc"
,perms
=>
mog
("600"
,"tzz"
,"tzz"
); }