Not to be outdone by Emacs, Vim can be equally productive in editing
CFEngine files. Here I’ll show you how to set up and get started with the
vim_cf3
plugin.
The vim_cf3
plugin can be found at https://github.com/neilhwatson/vim_cf3. Copy the relevant
parts into your $HOME/.vim/ directory:
The file type plugin for file editing: $HOME/.vim/ftplugin/cf3.vim.
The syntax highlighting file: $HOME/.vim/syntax/cf3.vim.
The help file: $HOME/.vim/doc/cf3.txt.
The help directory and its contents: $HOME/.vim/snippets.
Here is a minimum .vimrc to make the plugin work and associate it with filenames ending in .cf:
set smartindent set autoindent syntax on filetype plugin indent on :helptags ~/.vim/doc/ au BufRead,BufNewFile *.cf set ft=cf3 " enable vim_cf3 plugin abbreviations let g:EnableCFE3KeywordAbbreviations=1 fun! Getchar() let c = getchar() if c != 0 let c = nr2char(c) endif return c endfun fun! Eatchar(pat) let c = Getchar() return (c =~ a:pat) ? '' : c endfun
There is a Vim help file that comes with vim_cf3
.
In command mode, :help cf3
will call the help. You can
also be more specific (for example, :help cf3abb
) to
call help about abbreviations.
Once the plugin is installed you can begin using it. Edit any
exiting or new CFEngine file. The filename must end in
.cf in order for the plugin to recognize it (you can
also activate the plugin by hand on any file by executing :set
ft=cf3
). Let’s start with a blank file. Our first useful command
will build the skeleton for a self-contained CFEngine file. Self-contained
files, like this, are what I normally use in developing prototype or bug
reporting bundles. In normal mode, press the keys ,-k (comma,
k) in order. The following template is automatically inserted:
body
common
control
{bundlesequence
=>
{"main"
, };inputs
=>
{"cfengine_stdlib.cf"
, }; }bundle
agent
main
{methods
:"any"
usebundle
=>
test
; }bundle
agent
test
{ }
You’ll notice that there is syntax highlighting. Folding, which lets
you switch between overview and detailed views of functions, is also
enabled. If everything is folded at the moment, press z-R to
unfold. Any bundle or body can be folded. For example, in the text just
inserted, move the cursor inside the body of bundle agent
main
, anywhere between the curly brackets, and press z-c. You
should see the following:
+-- 7 lines: bundle agent main {
The bundle is now folded. You can open it with z-o. Everything in the current file can be unfolded with z-R. See Vim’s help on folding for more information about this topic.
When typing, you can take advantage of multiple helpful abbreviations, which can be seen in Table B-1. In insert mode, if you type any of the strings from the first column, then a space, the text will expand automatically to the second column.
Abbreviation | Expansion |
= | => |
ba | bundle agent |
bc | bundle common |
bu | bundle |
cano | canonify( " |
comma | commands: |
comme | comment => “ |
cla | classes: |
exp | expression => |
fil | files: |
han | handle => “ |
ifv | ifvarclass => |
met | methods: |
pro | processes: |
rep | reports: |
sli | slist => { |
str | string => “ |
sysw | ${sys.workdir} |
ub | usebundle => |
var | vars: |
Sometimes you may want to disable these abbreviations, such as during paste inserts. To do so, use the keys ,-i to toggle the abbreviations on and off.
There are a few other useful key sequences that operate in normal mode:
,-q wraps a word in quotes. The cursor needs to be at the beginning of the word to quote.
It can also quote a list of lines, when visually blocked. Every element is quoted, and a comma is added after each. This can be used to quickly turn a set of lines into a syntactically-correct CFEngine list. For example, if you have the following:
a sequence of lines that will become a list
Put the cursor on the first “a” and press v to enable visual-block mode. Now move the cursor to the word “list”, and press ,-q. The result is:
"a sequence", "of", "lines", "that will", "become", "a", "list",
,-p inserts a minimal blank promise template:
""
handle
=>
""
,comment
=>
""
,-= and Esc-=tidies your policy by aligning the => assignment symbols. For example, start with the following variable declarations:
vars
: "i
"slist
=>
getindices
("
${ref}
"
); "dir[${i}]
"string
=>
"
${${ref}[${i}][dir]}
"
; "url[${i}]
"string
=>
"
${${ref}[${i}][url]}
"
; "hours[${i}]
"string
=>
"
${${ref}[${i}][hours]}
"
; "target[${i}]
"string
=>
"
${${ref}[${i}][target]}
"
;
Place the cursor on any of the five variable declaration lines and press Esc-=. The code changes to the following:
vars
: "i
"slist
=>
getindices
("
${ref}
"
); "dir[${i}]
"string
=>
"
${${ref}[${i}][dir]}
"
; "url[${i}]
"string
=>
"
${${ref}[${i}][url]}
"
; "hours[${i}]
"string
=>
"
${${ref}[${i}][hours]}
"
; "target[${i}]
"string
=>
"
${${ref}[${i}][target]}
"
;
Note that both the variable types (string and
slist), and the =>
are aligned.
Other attributes are aligned on the =>
only. For
example, in this code:
"/tmp/testdelete"
comment
=>
"Garbage collection of any output files"
,delete
=>
tidy
,file_select
=>
days_old
("3"
),depth_search
=>
recurse
("inf"
);
Place the cursor on any of the four attribute lines and press ,-=. The result is as follows:
"/tmp/testdelete"
comment
=>
"Garbage collection of any output files"
,delete
=>
tidy
,file_select
=>
days_old
("3"
),depth_search
=>
recurse
("inf"
);
The code for this plugin is available on GitHub. You are welcome to use and modify it, either to customize it for your own needs, or to contribute your changes back to the code base as pull requests. The two main user-accessible components defined in the plugin are abbreviations and maps.
Abbreviations are found inside the
EnableCFE3KeywordAbbriveations()
function in the
ftplugin/cf3.vim file:
function! EnableCFE3KeywordAbbreviations() iab = => iab bu bundle iab han handle => "<C-R>=Eatchar('\s')<CR> iab cla classes: iab comma commands: iab comme comment => "<C-R>=Eatchar('\s')<CR> iab fil files: iab met methods: iab pro processes: iab rep reports: iab var vars: iab ba bundle agent iab bc bundle common iab ub usebundle => iab str string => "<C-R>=Eatchar('\s')<CR> iab sli slist => { echo "CFEngine 3 Keyword Abbreviations enabled" endfunction
This is where you can add or customize abbreviations. The
iab
command sets an insert abbreviation. The second
string is the text required to activate the abbreviation and the third
string is the expanded result. You can add or change them here as you
like. Notice that in some parts the Eatchar()
function is called. This is used to prevent a trailing space from being
appended to the abbreviation. Each line is a complete abbreviation. All
these abbreviations must also be added to
DisableCFE3KeywordAbbreviations()
, which is used to
disable all the abbreviations:
function! DisableCFE3KeywordAbbreviations() iunab = iunab bu iunab han iunab cla iunab comma iunab comme iunab fil iunab met iunab pro iunab rep iunab var iunab ba iunab bc iunab ub iunab str iunab sli echo "CFEngine 3 Keyword Abbreviations disabled" endfunction
These two functions are used by the ,-i shortcut mentioned earlier.
Maps are commands or functions that can be called with key strokes. Here is the definition of the ,-k map that can be used to create a self-contained CFEngine example:
nmap <buffer> ,k :read $HOME/.vim/snippets/template.cf<CR>kdd
The nmap
command defines a normal map. The
,k
that follows it are the key strokes that will be
used to activate the map.
There is a lot to learn about Vim. If you are interested in learning more you can consult the Vim help files or Learning the vi and Vim Editors by Arnold Robbins, Elbert Hannah, and Linda Lamb (O’Reilly).
Here is a CFEngine agent bundle that can be used to install the plugin on a machine so that all users have access to it:
bundle
agent
vim_cf3
{# Assumes that vimfiles source directory contains the following: # ./vimfiles/doc/cf3.txt # ./vimfiles/ftplugin/cf3.vim # ./vimfiles/syntax/cf3.vim # You need to customize the copy_from line according to your own # configuration
files
:"/usr/share/vim/vimfiles/."
perms
=>
mog
("0644"
,"root"
,"root"
),create
=>
"true"
,depth_search
=>
recurse
("inf"
),classes
=>
if_repaired
("vim_helptags"
),copy_from
=>
remote_dcp
("
${g.sitefiles}
/misc/vimfiles"
,"
${sys.policy_hub}
"
);commands
:vim_helptags
::"/bin/echo ':helptags /usr/share/vim/vimfiles/doc'|/usr/bin/vim -es -"
comment
=>
"Generate help tags"
,contain
=>
in_shell_and_silent
; }