;;; cfengine.el --- mode for editing Cfengine files
-;; Copyright (C) 2001-2013 Free Software Foundation, Inc.
+;; Copyright (C) 2001-2016 Free Software Foundation, Inc.
;; Author: Dave Love <fx@gnu.org>
;; Maintainer: Ted Zlatanov <tzz@lifelogs.com>
;; Keywords: languages
-;; Version: 1.3
+;; Version: 1.4
;; This file is part of GNU Emacs.
;;; Commentary:
-;; Provides support for editing GNU Cfengine files, including
+;; Provides support for editing GNU CFEngine files, including
;; font-locking, Imenu and indentation, but with no special keybindings.
-;; The CFEngine 3.x support doesn't have Imenu support but patches are
-;; welcome.
-
;; By default, CFEngine 3.x syntax is used.
;; You can set it up so either `cfengine2-mode' (2.x and earlier) or
;; It's *highly* recommended that you enable the eldoc minor mode:
-;; (add-hook 'cfengine-mode-hook 'turn-on-eldoc-mode)
+;; (add-hook 'cfengine3-mode-hook 'eldoc-mode)
+
+;; You may also find the command `cfengine3-reformat-json-string'
+;; useful, just bind it to a key you prefer. It will take the current
+;; string and reformat it as JSON. So if you're editing JSON inside
+;; the policy, it's a quick way to make it more legible without
+;; manually reindenting it. For instance:
+
+;; (global-set-key [(control f4)] 'cfengine3-reformat-json-string)
;; This is not the same as the mode written by Rolf Ebert
;; <ebert@waporo.muc.de>, distributed with cfengine-2.0.5. It does
;;; Code:
+(autoload 'json-read "json")
+(autoload 'json-pretty-print "json")
+
(defgroup cfengine ()
"Editing CFEngine files."
:group 'languages)
(or (executable-find "cf-promises")
(executable-find "/var/cfengine/bin/cf-promises")
(executable-find "/usr/bin/cf-promises")
+ (executable-find "/usr/sbin/cf-promises")
(executable-find "/usr/local/bin/cf-promises")
- (executable-find "~/bin/cf-promises"))
+ (executable-find "/usr/local/sbin/cf-promises")
+ (executable-find "~/bin/cf-promises")
+ (executable-find "~/sbin/cf-promises"))
"The location of the cf-promises executable.
Used for syntax discovery and checking. Set to nil to disable
-the `compile-command' override and the ElDoc support."
+the `compile-command' override. In that case, the ElDoc support
+will use a fallback syntax definition."
+ :version "24.4"
:group 'cfengine
- :type 'file)
+ :type '(choice file (const nil)))
-(defcustom cfengine-parameters-indent '(promise pname 0)
- "*Indentation of CFEngine3 promise parameters (hanging indent).
+(defcustom cfengine-parameters-indent '(promise pname 2)
+ "Indentation of CFEngine3 promise parameters (hanging indent).
For example, say you have this code:
Finally, you can choose the amount of the indent.
-The default is to anchor at promise, indent parameter name, and offset 0:
+The default is to anchor at promise, indent parameter name, and offset 2:
bundle agent rcfiles
{
files:
any::
\"/tmp/netrc\"
- comment => \"my netrc\",
- perms => mog(\"600\", \"tzz\", \"tzz\");
+ comment => \"my netrc\",
+ perms => mog(\"600\", \"tzz\", \"tzz\");
}
Here we anchor at beginning of line, indent arrow, and offset 10:
perms => mog(\"600\", \"tzz\", \"tzz\");
}
"
-
+ :version "24.4"
:group 'cfengine
:type '(list
(choice (const :tag "Anchor at beginning of promise" promise)
"Whether `cfengine-mode' should print debugging info.")
(defvar cfengine-mode-syntax-cache nil
- "Cache for `cfengine-mode' syntax trees obtained from 'cf-promises -s json'.")
+ "Cache for `cfengine-mode' syntax trees obtained from `cf-promises -s json'.")
+
+(defconst cfengine3-fallback-syntax
+ '((functions
+ (userexists
+ (category . "system") (variadic . :json-false)
+ (parameters . [((range . ".*") (type . "string"))])
+ (returnType . "context") (status . "normal"))
+ (usemodule
+ (category . "utils") (variadic . :json-false)
+ (parameters . [((range . ".*") (type . "string"))
+ ((range . ".*") (type . "string"))])
+ (returnType . "context") (status . "normal"))
+ (unique
+ (category . "data") (variadic . :json-false)
+ (parameters . [((range . "[a-zA-Z0-9_$(){}\\[\\].:]+") (type . "string"))])
+ (returnType . "slist") (status . "normal"))
+ (translatepath
+ (category . "files") (variadic . :json-false)
+ (parameters . [((range . "\"?(/.*)") (type . "string"))])
+ (returnType . "string") (status . "normal"))
+ (sum
+ (category . "data") (variadic . :json-false)
+ (parameters . [((range . "[a-zA-Z0-9_$(){}\\[\\].:]+") (type . "string"))])
+ (returnType . "real") (status . "normal"))
+ (sublist
+ (category . "data") (variadic . :json-false)
+ (parameters . [((range . "[a-zA-Z0-9_$(){}\\[\\].:]+") (type . "string"))
+ ((range . "head,tail") (type . "option"))
+ ((range . "0,99999999999") (type . "int"))])
+ (returnType . "slist") (status . "normal"))
+ (strftime
+ (category . "data") (variadic . :json-false)
+ (parameters . [((range . "gmtime,localtime") (type . "option"))
+ ((range . ".*") (type . "string"))
+ ((range . "0,99999999999") (type . "int"))])
+ (returnType . "string") (status . "normal"))
+ (strcmp
+ (category . "data") (variadic . :json-false)
+ (parameters . [((range . ".*") (type . "string"))
+ ((range . ".*") (type . "string"))])
+ (returnType . "context") (status . "normal"))
+ (splitstring
+ (category . "data") (variadic . :json-false)
+ (parameters . [((range . ".*") (type . "string"))
+ ((range . ".*") (type . "string"))
+ ((range . "0,99999999999") (type . "int"))])
+ (returnType . "slist") (status . "normal"))
+ (splayclass
+ (category . "utils") (variadic . :json-false)
+ (parameters . [((range . ".*") (type . "string"))
+ ((range . "daily,hourly") (type . "option"))])
+ (returnType . "context") (status . "normal"))
+ (sort
+ (category . "data") (variadic . :json-false)
+ (parameters . [((range . "[a-zA-Z0-9_$(){}\\[\\].:]+") (type . "string"))
+ ((range . "lex") (type . "string"))])
+ (returnType . "slist") (status . "normal"))
+ (some
+ (category . "data") (variadic . :json-false)
+ (parameters . [((range . ".*") (type . "string"))
+ ((range . "[a-zA-Z0-9_$(){}\\[\\].:]+") (type . "string"))])
+ (returnType . "context") (status . "normal"))
+ (shuffle
+ (category . "data") (variadic . :json-false)
+ (parameters . [((range . "[a-zA-Z0-9_$(){}\\[\\].:]+") (type . "string"))
+ ((range . ".*") (type . "string"))])
+ (returnType . "slist") (status . "normal"))
+ (selectservers
+ (category . "communication") (variadic . :json-false)
+ (parameters . [((range . "@[(][a-zA-Z0-9]+[)]") (type . "string"))
+ ((range . "0,99999999999") (type . "int"))
+ ((range . ".*") (type . "string"))
+ ((range . ".*") (type . "string"))
+ ((range . "0,99999999999") (type . "int"))
+ ((range . "[a-zA-Z0-9_$(){}\\[\\].:]+") (type . "string"))])
+ (returnType . "int") (status . "normal"))
+ (reverse
+ (category . "data") (variadic . :json-false)
+ (parameters . [((range . "[a-zA-Z0-9_$(){}\\[\\].:]+") (type . "string"))])
+ (returnType . "slist") (status . "normal"))
+ (rrange
+ (category . "data") (variadic . :json-false)
+ (parameters . [((range . "-9.99999E100,9.99999E100") (type . "real"))
+ ((range . "-9.99999E100,9.99999E100") (type . "real"))])
+ (returnType . "rrange") (status . "normal"))
+ (returnszero
+ (category . "utils") (variadic . :json-false)
+ (parameters . [((range . "\"?(/.*)") (type . "string"))
+ ((range . "useshell,noshell,powershell") (type . "option"))])
+ (returnType . "context") (status . "normal"))
+ (remoteclassesmatching
+ (category . "communication") (variadic . :json-false)
+ (parameters . [((range . ".*") (type . "string"))
+ ((range . ".*") (type . "string"))
+ ((range . "true,false,yes,no,on,off") (type . "option"))
+ ((range . "[a-zA-Z0-9_$(){}\\[\\].:]+") (type . "string"))])
+ (returnType . "context") (status . "normal"))
+ (remotescalar
+ (category . "communication") (variadic . :json-false)
+ (parameters . [((range . "[a-zA-Z0-9_$(){}\\[\\].:]+") (type . "string"))
+ ((range . ".*") (type . "string"))
+ ((range . "true,false,yes,no,on,off") (type . "option"))])
+ (returnType . "string") (status . "normal"))
+ (regldap
+ (category . "communication") (variadic . :json-false)
+ (parameters . [((range . ".*") (type . "string"))
+ ((range . ".*") (type . "string"))
+ ((range . ".*") (type . "string"))
+ ((range . ".*") (type . "string"))
+ ((range . "subtree,onelevel,base") (type . "option"))
+ ((range . ".*") (type . "string"))
+ ((range . "none,ssl,sasl") (type . "option"))])
+ (returnType . "context") (status . "normal"))
+ (reglist
+ (category . "data") (variadic . :json-false)
+ (parameters . [((range . "@[(][a-zA-Z0-9]+[)]") (type . "string"))
+ ((range . ".*") (type . "string"))])
+ (returnType . "context") (status . "normal"))
+ (regline
+ (category . "io") (variadic . :json-false)
+ (parameters . [((range . ".*") (type . "string"))
+ ((range . ".*") (type . "string"))])
+ (returnType . "context") (status . "normal"))
+ (registryvalue
+ (category . "system") (variadic . :json-false)
+ (parameters . [((range . ".*") (type . "string"))
+ ((range . ".*") (type . "string"))])
+ (returnType . "string") (status . "normal"))
+ (regextract
+ (category . "data") (variadic . :json-false)
+ (parameters . [((range . ".*") (type . "string"))
+ ((range . ".*") (type . "string"))
+ ((range . "[a-zA-Z0-9_$(){}\\[\\].:]+") (type . "string"))])
+ (returnType . "context") (status . "normal"))
+ (regcmp
+ (category . "data") (variadic . :json-false)
+ (parameters . [((range . ".*") (type . "string"))
+ ((range . ".*") (type . "string"))])
+ (returnType . "context") (status . "normal"))
+ (regarray
+ (category . "data") (variadic . :json-false)
+ (parameters . [((range . "[a-zA-Z0-9_$(){}\\[\\].:]+") (type . "string"))
+ ((range . ".*") (type . "string"))])
+ (returnType . "context") (status . "normal"))
+ (readtcp
+ (category . "communication") (variadic . :json-false)
+ (parameters . [((range . ".*") (type . "string"))
+ ((range . "0,99999999999") (type . "int"))
+ ((range . ".*") (type . "string"))
+ ((range . "0,99999999999") (type . "int"))])
+ (returnType . "string") (status . "normal"))
+ (readstringlist
+ (category . "io") (variadic . :json-false)
+ (parameters . [((range . "\"?(/.*)") (type . "string"))
+ ((range . ".*") (type . "string"))
+ ((range . ".*") (type . "string"))
+ ((range . "0,99999999999") (type . "int"))
+ ((range . "0,99999999999") (type . "int"))])
+ (returnType . "slist") (status . "normal"))
+ (readstringarrayidx
+ (category . "io") (variadic . :json-false)
+ (parameters . [((range . "[a-zA-Z0-9_$(){}\\[\\].:]+") (type . "string"))
+ ((range . "\"?(/.*)") (type . "string"))
+ ((range . ".*") (type . "string"))
+ ((range . ".*") (type . "string"))
+ ((range . "0,99999999999") (type . "int"))
+ ((range . "0,99999999999") (type . "int"))])
+ (returnType . "int") (status . "normal"))
+ (readstringarray
+ (category . "io") (variadic . :json-false)
+ (parameters . [((range . "[a-zA-Z0-9_$(){}\\[\\].:]+") (type . "string"))
+ ((range . "\"?(/.*)") (type . "string"))
+ ((range . ".*") (type . "string"))
+ ((range . ".*") (type . "string"))
+ ((range . "0,99999999999") (type . "int"))
+ ((range . "0,99999999999") (type . "int"))])
+ (returnType . "int") (status . "normal"))
+ (readreallist
+ (category . "io") (variadic . :json-false)
+ (parameters . [((range . "\"?(/.*)") (type . "string"))
+ ((range . ".*") (type . "string"))
+ ((range . ".*") (type . "string"))
+ ((range . "0,99999999999") (type . "int"))
+ ((range . "0,99999999999") (type . "int"))])
+ (returnType . "rlist") (status . "normal"))
+ (readrealarray
+ (category . "io") (variadic . :json-false)
+ (parameters . [((range . "[a-zA-Z0-9_$(){}\\[\\].:]+") (type . "string"))
+ ((range . "\"?(/.*)") (type . "string"))
+ ((range . ".*") (type . "string"))
+ ((range . ".*") (type . "string"))
+ ((range . "0,99999999999") (type . "int"))
+ ((range . "0,99999999999") (type . "int"))])
+ (returnType . "int") (status . "normal"))
+ (readintlist
+ (category . "io") (variadic . :json-false)
+ (parameters . [((range . "\"?(/.*)") (type . "string"))
+ ((range . ".*") (type . "string"))
+ ((range . ".*") (type . "string"))
+ ((range . "0,99999999999") (type . "int"))
+ ((range . "0,99999999999") (type . "int"))])
+ (returnType . "ilist") (status . "normal"))
+ (readintarray
+ (category . "io") (variadic . :json-false)
+ (parameters . [((range . "[a-zA-Z0-9_$(){}\\[\\].:]+") (type . "string"))
+ ((range . "\"?(/.*)") (type . "string"))
+ ((range . ".*") (type . "string"))
+ ((range . ".*") (type . "string"))
+ ((range . "0,99999999999") (type . "int"))
+ ((range . "0,99999999999") (type . "int"))])
+ (returnType . "int") (status . "normal"))
+ (readfile
+ (category . "io") (variadic . :json-false)
+ (parameters . [((range . "\"?(/.*)") (type . "string"))
+ ((range . "0,99999999999") (type . "int"))])
+ (returnType . "string") (status . "normal"))
+ (randomint
+ (category . "data") (variadic . :json-false)
+ (parameters . [((range . "-99999999999,9999999999") (type . "int"))
+ ((range . "-99999999999,9999999999") (type . "int"))])
+ (returnType . "int") (status . "normal"))
+ (product
+ (category . "data") (variadic . :json-false)
+ (parameters . [((range . "[a-zA-Z0-9_$(){}\\[\\].:]+") (type . "string"))])
+ (returnType . "real") (status . "normal"))
+ (peerleaders
+ (category . "communication") (variadic . :json-false)
+ (parameters . [((range . "\"?(/.*)") (type . "string"))
+ ((range . ".*") (type . "string"))
+ ((range . "0,99999999999") (type . "int"))])
+ (returnType . "slist") (status . "normal"))
+ (peerleader
+ (category . "communication") (variadic . :json-false)
+ (parameters . [((range . "\"?(/.*)") (type . "string"))
+ ((range . ".*") (type . "string"))
+ ((range . "0,99999999999") (type . "int"))])
+ (returnType . "string") (status . "normal"))
+ (peers
+ (category . "communication") (variadic . :json-false)
+ (parameters . [((range . "\"?(/.*)") (type . "string"))
+ ((range . ".*") (type . "string"))
+ ((range . "0,99999999999") (type . "int"))])
+ (returnType . "slist") (status . "normal"))
+ (parsestringarrayidx
+ (category . "io") (variadic . :json-false)
+ (parameters . [((range . "[a-zA-Z0-9_$(){}\\[\\].:]+") (type . "string"))
+ ((range . "\"?(/.*)") (type . "string"))
+ ((range . ".*") (type . "string"))
+ ((range . ".*") (type . "string"))
+ ((range . "0,99999999999") (type . "int"))
+ ((range . "0,99999999999") (type . "int"))])
+ (returnType . "int") (status . "normal"))
+ (parsestringarray
+ (category . "io") (variadic . :json-false)
+ (parameters . [((range . "[a-zA-Z0-9_$(){}\\[\\].:]+") (type . "string"))
+ ((range . "\"?(/.*)") (type . "string"))
+ ((range . ".*") (type . "string"))
+ ((range . ".*") (type . "string"))
+ ((range . "0,99999999999") (type . "int"))
+ ((range . "0,99999999999") (type . "int"))])
+ (returnType . "int") (status . "normal"))
+ (parserealarray
+ (category . "io") (variadic . :json-false)
+ (parameters . [((range . "[a-zA-Z0-9_$(){}\\[\\].:]+") (type . "string"))
+ ((range . "\"?(/.*)") (type . "string"))
+ ((range . ".*") (type . "string"))
+ ((range . ".*") (type . "string"))
+ ((range . "0,99999999999") (type . "int"))
+ ((range . "0,99999999999") (type . "int"))])
+ (returnType . "int") (status . "normal"))
+ (parseintarray
+ (category . "io") (variadic . :json-false)
+ (parameters . [((range . "[a-zA-Z0-9_$(){}\\[\\].:]+") (type . "string"))
+ ((range . "\"?(/.*)") (type . "string"))
+ ((range . ".*") (type . "string"))
+ ((range . ".*") (type . "string"))
+ ((range . "0,99999999999") (type . "int"))
+ ((range . "0,99999999999") (type . "int"))])
+ (returnType . "int") (status . "normal"))
+ (or
+ (category . "data") (variadic . t)
+ (parameters . [])
+ (returnType . "string") (status . "normal"))
+ (on
+ (category . "data") (variadic . :json-false)
+ (parameters . [((range . "1970,3000") (type . "int"))
+ ((range . "1,12") (type . "int"))
+ ((range . "1,31") (type . "int"))
+ ((range . "0,23") (type . "int"))
+ ((range . "0,59") (type . "int"))
+ ((range . "0,59") (type . "int"))])
+ (returnType . "int") (status . "normal"))
+ (nth
+ (category . "data") (variadic . :json-false)
+ (parameters . [((range . "[a-zA-Z0-9_$(){}\\[\\].:]+") (type . "string"))
+ ((range . "0,99999999999") (type . "int"))])
+ (returnType . "string") (status . "normal"))
+ (now
+ (category . "system") (variadic . :json-false)
+ (parameters . [])
+ (returnType . "int") (status . "normal"))
+ (not
+ (category . "data") (variadic . :json-false)
+ (parameters . [((range . ".*") (type . "string"))])
+ (returnType . "string") (status . "normal"))
+ (none
+ (category . "data") (variadic . :json-false)
+ (parameters . [((range . ".*") (type . "string"))
+ ((range . "[a-zA-Z0-9_$(){}\\[\\].:]+") (type . "string"))])
+ (returnType . "context") (status . "normal"))
+ (maplist
+ (category . "data") (variadic . :json-false)
+ (parameters . [((range . ".*") (type . "string"))
+ ((range . "[a-zA-Z0-9_$(){}\\[\\].:]+") (type . "string"))])
+ (returnType . "slist") (status . "normal"))
+ (maparray
+ (category . "data") (variadic . :json-false)
+ (parameters . [((range . ".*") (type . "string"))
+ ((range . "[a-zA-Z0-9_$(){}\\[\\].:]+") (type . "string"))])
+ (returnType . "slist") (status . "normal"))
+ (lsdir
+ (category . "files") (variadic . :json-false)
+ (parameters . [((range . ".+") (type . "string"))
+ ((range . ".*") (type . "string"))
+ ((range . "true,false,yes,no,on,off") (type . "option"))])
+ (returnType . "slist") (status . "normal"))
+ (length
+ (category . "data") (variadic . :json-false)
+ (parameters . [((range . "[a-zA-Z0-9_$(){}\\[\\].:]+") (type . "string"))])
+ (returnType . "int") (status . "normal"))
+ (ldapvalue
+ (category . "communication") (variadic . :json-false)
+ (parameters . [((range . ".*") (type . "string"))
+ ((range . ".*") (type . "string"))
+ ((range . ".*") (type . "string"))
+ ((range . ".*") (type . "string"))
+ ((range . "subtree,onelevel,base") (type . "option"))
+ ((range . "none,ssl,sasl") (type . "option"))])
+ (returnType . "string") (status . "normal"))
+ (ldaplist
+ (category . "communication") (variadic . :json-false)
+ (parameters . [((range . ".*") (type . "string"))
+ ((range . ".*") (type . "string"))
+ ((range . ".*") (type . "string"))
+ ((range . ".*") (type . "string"))
+ ((range . "subtree,onelevel,base") (type . "option"))
+ ((range . "none,ssl,sasl") (type . "option"))])
+ (returnType . "slist") (status . "normal"))
+ (ldaparray
+ (category . "communication") (variadic . :json-false)
+ (parameters . [((range . ".*") (type . "string"))
+ ((range . ".*") (type . "string"))
+ ((range . ".*") (type . "string"))
+ ((range . ".*") (type . "string"))
+ ((range . "subtree,onelevel,base") (type . "option"))
+ ((range . "none,ssl,sasl") (type . "option"))])
+ (returnType . "context") (status . "normal"))
+ (laterthan
+ (category . "files") (variadic . :json-false)
+ (parameters . [((range . "0,1000") (type . "int"))
+ ((range . "0,1000") (type . "int"))
+ ((range . "0,1000") (type . "int"))
+ ((range . "0,1000") (type . "int"))
+ ((range . "0,1000") (type . "int"))
+ ((range . "0,40000") (type . "int"))])
+ (returnType . "context") (status . "normal"))
+ (lastnode
+ (category . "data") (variadic . :json-false)
+ (parameters . [((range . ".*") (type . "string"))
+ ((range . ".*") (type . "string"))])
+ (returnType . "string") (status . "normal"))
+ (join
+ (category . "data") (variadic . :json-false)
+ (parameters . [((range . ".*") (type . "string"))
+ ((range . "[a-zA-Z0-9_$(){}\\[\\].:]+") (type . "string"))])
+ (returnType . "string") (status . "normal"))
+ (isvariable
+ (category . "utils") (variadic . :json-false)
+ (parameters . [((range . "[a-zA-Z0-9_$(){}\\[\\].:]+") (type . "string"))])
+ (returnType . "context") (status . "normal"))
+ (isplain
+ (category . "files") (variadic . :json-false)
+ (parameters . [((range . "\"?(/.*)") (type . "string"))])
+ (returnType . "context") (status . "normal"))
+ (isnewerthan
+ (category . "files") (variadic . :json-false)
+ (parameters . [((range . "\"?(/.*)") (type . "string"))
+ ((range . "\"?(/.*)") (type . "string"))])
+ (returnType . "context") (status . "normal"))
+ (islink
+ (category . "files") (variadic . :json-false)
+ (parameters . [((range . "\"?(/.*)") (type . "string"))])
+ (returnType . "context") (status . "normal"))
+ (islessthan
+ (category . "data") (variadic . :json-false)
+ (parameters . [((range . ".*") (type . "string"))
+ ((range . ".*") (type . "string"))])
+ (returnType . "context") (status . "normal"))
+ (isgreaterthan
+ (category . "data") (variadic . :json-false)
+ (parameters . [((range . ".*") (type . "string"))
+ ((range . ".*") (type . "string"))])
+ (returnType . "context") (status . "normal"))
+ (isexecutable
+ (category . "files") (variadic . :json-false)
+ (parameters . [((range . "\"?(/.*)") (type . "string"))])
+ (returnType . "context") (status . "normal"))
+ (isdir
+ (category . "files") (variadic . :json-false)
+ (parameters . [((range . "\"?(/.*)") (type . "string"))])
+ (returnType . "context") (status . "normal"))
+ (irange
+ (category . "data") (variadic . :json-false)
+ (parameters . [((range . "-99999999999,9999999999") (type . "int"))
+ ((range . "-99999999999,9999999999") (type . "int"))])
+ (returnType . "irange") (status . "normal"))
+ (iprange
+ (category . "communication") (variadic . :json-false)
+ (parameters . [((range . ".*") (type . "string"))])
+ (returnType . "context") (status . "normal"))
+ (intersection
+ (category . "data") (variadic . :json-false)
+ (parameters . [((range . "[a-zA-Z0-9_$(){}\\[\\].:]+") (type . "string"))
+ ((range . "[a-zA-Z0-9_$(){}\\[\\].:]+") (type . "string"))])
+ (returnType . "slist") (status . "normal"))
+ (ifelse
+ (category . "data") (variadic . t)
+ (parameters . [])
+ (returnType . "string") (status . "normal"))
+ (hubknowledge
+ (category . "communication") (variadic . :json-false)
+ (parameters . [((range . "[a-zA-Z0-9_$(){}\\[\\].:]+") (type . "string"))])
+ (returnType . "string") (status . "normal"))
+ (hostswithclass
+ (category . "communication") (variadic . :json-false)
+ (parameters . [((range . "[a-zA-Z0-9_]+") (type . "string"))
+ ((range . "name,address") (type . "option"))])
+ (returnType . "slist") (status . "normal"))
+ (hostsseen
+ (category . "communication") (variadic . :json-false)
+ (parameters . [((range . "0,99999999999") (type . "int"))
+ ((range . "lastseen,notseen") (type . "option"))
+ ((range . "name,address") (type . "option"))])
+ (returnType . "slist") (status . "normal"))
+ (hostrange
+ (category . "communication") (variadic . :json-false)
+ (parameters . [((range . ".*") (type . "string"))
+ ((range . ".*") (type . "string"))])
+ (returnType . "context") (status . "normal"))
+ (hostinnetgroup
+ (category . "system") (variadic . :json-false)
+ (parameters . [((range . ".*") (type . "string"))])
+ (returnType . "context") (status . "normal"))
+ (ip2host
+ (category . "communication") (variadic . :json-false)
+ (parameters . [((range . ".*") (type . "string"))])
+ (returnType . "string") (status . "normal"))
+ (host2ip
+ (category . "communication") (variadic . :json-false)
+ (parameters . [((range . ".*") (type . "string"))])
+ (returnType . "string") (status . "normal"))
+ (hashmatch
+ (category . "data") (variadic . :json-false)
+ (parameters . [((range . "\"?(/.*)") (type . "string"))
+ ((range . "md5,sha1,crypt,cf_sha224,cf_sha256,cf_sha384,cf_sha512") (type . "option"))
+ ((range . "[a-zA-Z0-9_$(){}\\[\\].:]+") (type . "string"))])
+ (returnType . "context") (status . "normal"))
+ (hash
+ (category . "data") (variadic . :json-false)
+ (parameters . [((range . ".*") (type . "string"))
+ ((range . "md5,sha1,sha256,sha512,sha384,crypt") (type . "option"))])
+ (returnType . "string") (status . "normal"))
+ (groupexists
+ (category . "system") (variadic . :json-false)
+ (parameters . [((range . ".*") (type . "string"))])
+ (returnType . "context") (status . "normal"))
+ (grep
+ (category . "data") (variadic . :json-false)
+ (parameters . [((range . ".*") (type . "string"))
+ ((range . "[a-zA-Z0-9_$(){}\\[\\].:]+") (type . "string"))])
+ (returnType . "slist") (status . "normal"))
+ (getvalues
+ (category . "data") (variadic . :json-false)
+ (parameters . [((range . "[a-zA-Z0-9_$(){}\\[\\].:]+") (type . "string"))])
+ (returnType . "slist") (status . "normal"))
+ (getusers
+ (category . "system") (variadic . :json-false)
+ (parameters . [((range . ".*") (type . "string"))
+ ((range . ".*") (type . "string"))])
+ (returnType . "slist") (status . "normal"))
+ (getuid
+ (category . "system") (variadic . :json-false)
+ (parameters . [((range . ".*") (type . "string"))])
+ (returnType . "int") (status . "normal"))
+ (getindices
+ (category . "data") (variadic . :json-false)
+ (parameters . [((range . "[a-zA-Z0-9_$(){}\\[\\].:]+") (type . "string"))])
+ (returnType . "slist") (status . "normal"))
+ (getgid
+ (category . "data") (variadic . :json-false)
+ (parameters . [((range . ".*") (type . "string"))])
+ (returnType . "int") (status . "normal"))
+ (getfields
+ (category . "data") (variadic . :json-false)
+ (parameters . [((range . ".*") (type . "string"))
+ ((range . "\"?(/.*)") (type . "string"))
+ ((range . ".*") (type . "string"))
+ ((range . ".*") (type . "string"))])
+ (returnType . "int") (status . "normal"))
+ (getenv
+ (category . "system") (variadic . :json-false)
+ (parameters . [((range . "[a-zA-Z0-9_$(){}\\[\\].:]+") (type . "string"))
+ ((range . "0,99999999999") (type . "int"))])
+ (returnType . "string") (status . "normal"))
+ (format
+ (category . "data") (variadic . t)
+ (parameters . [((range . ".*") (type . "string"))])
+ (returnType . "string") (status . "normal"))
+ (filter
+ (category . "data") (variadic . :json-false)
+ (parameters . [((range . ".*") (type . "string"))
+ ((range . "[a-zA-Z0-9_$(){}\\[\\].:]+") (type . "string"))
+ ((range . "true,false,yes,no,on,off") (type . "option"))
+ ((range . "true,false,yes,no,on,off") (type . "option"))
+ ((range . "0,99999999999") (type . "int"))])
+ (returnType . "slist") (status . "normal"))
+ (filestat
+ (category . "files") (variadic . :json-false)
+ (parameters . [((range . "\"?(/.*)") (type . "string"))
+ ((range . "size,gid,uid,ino,nlink,ctime,atime,mtime,mode,modeoct,permstr,permoct,type,devno,dev_minor,dev_major,basename,dirname") (type . "option"))])
+ (returnType . "string") (status . "normal"))
+ (filesize
+ (category . "files") (variadic . :json-false)
+ (parameters . [((range . "\"?(/.*)") (type . "string"))])
+ (returnType . "int") (status . "normal"))
+ (filesexist
+ (category . "files") (variadic . :json-false)
+ (parameters . [((range . "@[(][a-zA-Z0-9]+[)]") (type . "string"))])
+ (returnType . "context") (status . "normal"))
+ (fileexists
+ (category . "files") (variadic . :json-false)
+ (parameters . [((range . "\"?(/.*)") (type . "string"))])
+ (returnType . "context") (status . "normal"))
+ (execresult
+ (category . "utils") (variadic . :json-false)
+ (parameters . [((range . ".+") (type . "string"))
+ ((range . "useshell,noshell,powershell") (type . "option"))])
+ (returnType . "string") (status . "normal"))
+ (every
+ (category . "data") (variadic . :json-false)
+ (parameters . [((range . ".*") (type . "string"))
+ ((range . "[a-zA-Z0-9_$(){}\\[\\].:]+") (type . "string"))])
+ (returnType . "context") (status . "normal"))
+ (escape
+ (category . "data") (variadic . :json-false)
+ (parameters . [((range . ".*") (type . "string"))])
+ (returnType . "string") (status . "normal"))
+ (diskfree
+ (category . "files") (variadic . :json-false)
+ (parameters . [((range . "\"?(/.*)") (type . "string"))])
+ (returnType . "int") (status . "normal"))
+ (dirname
+ (category . "files") (variadic . :json-false)
+ (parameters . [((range . ".*") (type . "string"))])
+ (returnType . "string") (status . "normal"))
+ (difference
+ (category . "data") (variadic . :json-false)
+ (parameters . [((range . "[a-zA-Z0-9_$(){}\\[\\].:]+") (type . "string"))
+ ((range . "[a-zA-Z0-9_$(){}\\[\\].:]+") (type . "string"))])
+ (returnType . "slist") (status . "normal"))
+ (countlinesmatching
+ (category . "io") (variadic . :json-false)
+ (parameters . [((range . ".*") (type . "string"))
+ ((range . "\"?(/.*)") (type . "string"))])
+ (returnType . "int") (status . "normal"))
+ (countclassesmatching
+ (category . "utils") (variadic . :json-false)
+ (parameters . [((range . ".*") (type . "string"))])
+ (returnType . "int") (status . "normal"))
+ (classesmatching
+ (category . "utils") (variadic . :json-false)
+ (parameters . [((range . ".*") (type . "string"))])
+ (returnType . "slist") (status . "normal"))
+ (classmatch
+ (category . "utils") (variadic . :json-false)
+ (parameters . [((range . ".*") (type . "string"))])
+ (returnType . "context") (status . "normal"))
+ (classify
+ (category . "data") (variadic . :json-false)
+ (parameters . [((range . ".*") (type . "string"))])
+ (returnType . "context") (status . "normal"))
+ (changedbefore
+ (category . "files") (variadic . :json-false)
+ (parameters . [((range . "\"?(/.*)") (type . "string"))
+ ((range . "\"?(/.*)") (type . "string"))])
+ (returnType . "context") (status . "normal"))
+ (concat
+ (category . "data") (variadic . t)
+ (parameters . [])
+ (returnType . "string") (status . "normal"))
+ (canonify
+ (category . "data") (variadic . :json-false)
+ (parameters . [((range . ".*") (type . "string"))])
+ (returnType . "string") (status . "normal"))
+ (and
+ (category . "data") (variadic . t)
+ (parameters . [])
+ (returnType . "string") (status . "normal"))
+ (ago
+ (category . "data") (variadic . :json-false)
+ (parameters . [((range . "0,1000") (type . "int"))
+ ((range . "0,1000") (type . "int"))
+ ((range . "0,1000") (type . "int"))
+ ((range . "0,1000") (type . "int"))
+ ((range . "0,1000") (type . "int"))
+ ((range . "0,40000") (type . "int"))])
+ (returnType . "int") (status . "normal"))
+ (accumulated
+ (category . "data") (variadic . :json-false)
+ (parameters . [((range . "0,1000") (type . "int"))
+ ((range . "0,1000") (type . "int"))
+ ((range . "0,1000") (type . "int"))
+ ((range . "0,1000") (type . "int"))
+ ((range . "0,1000") (type . "int"))
+ ((range . "0,40000") (type . "int"))])
+ (returnType . "int") (status . "normal"))
+ (accessedbefore
+ (category . "files") (variadic . :json-false)
+ (parameters . [((range . "\"?(/.*)") (type . "string"))
+ ((range . "\"?(/.*)") (type . "string"))])
+ (returnType . "context") (status . "normal"))))
+ "Fallback CFEngine syntax, containing just function definitions.")
+
+(defvar cfengine-mode-syntax-functions-regex
+ (regexp-opt (mapcar (lambda (def)
+ (format "%s" (car def)))
+ (cdr (assq 'functions cfengine3-fallback-syntax)))
+ 'symbols))
(defcustom cfengine-mode-abbrevs nil
"Abbrevs for CFEngine2 mode."
"List of the action keywords supported by Cfengine.
This includes those for cfservd as well as cfagent.")
- (defconst cfengine3-defuns
- (mapcar
- 'symbol-name
- '(bundle body))
+ (defconst cfengine3-defuns '("bundle" "body")
"List of the CFEngine 3.x defun headings.")
- (defconst cfengine3-defuns-regex
- (regexp-opt cfengine3-defuns t)
+ (defconst cfengine3-defuns-regex (regexp-opt cfengine3-defuns t)
"Regex to match the CFEngine 3.x defuns.")
- (defconst cfengine3-class-selector-regex "\\([[:alnum:]_().&|!:]+\\)::")
+ (defconst cfengine3-defun-full-re (concat "^\\s-*" cfengine3-defuns-regex
+ "\\s-+\\(\\(?:\\w\\|\\s_\\)+\\)" ;type
+ "\\s-+\\(\\(?:\\w\\|\\s_\\)+\\)" ;id
+ )
+ "Regexp matching full defun declaration (excluding argument list).")
+
+ (defconst cfengine3-macro-regex "\\(@[a-zA-Z].+\\)")
+
+ (defconst cfengine3-class-selector-regex "\\([\"']?[[:alnum:]_().$&|!:]+[\"']?\\)::")
(defconst cfengine3-category-regex "\\([[:alnum:]_]+\\):")
- (defconst cfengine3-vartypes
- (mapcar
- 'symbol-name
- '(string int real slist ilist rlist irange rrange counter data))
+ (defconst cfengine3-vartypes '("string" "int" "real" "slist" "ilist" "rlist"
+ "irange" "rrange" "counter" "data")
"List of the CFEngine 3.x variable types."))
(defvar cfengine2-font-lock-keywords
(defvar cfengine3-font-lock-keywords
`(
+ ;; Macros
+ (,(concat "^" cfengine3-macro-regex)
+ 1 font-lock-error-face)
+
+ ;; invalid macros
+ (,(concat "^[ \t]*" cfengine3-macro-regex)
+ 1 font-lock-warning-face)
+
;; Defuns. This happens early so they don't get caught by looser
;; patterns.
(,(concat "\\_<" cfengine3-defuns-regex "\\_>"
(point))))
(let ((paragraph-start
;; Include start of parenthesized block.
- "\f\\|[ \t]*$\\|.*\(")
+ "\f\\|[ \t]*$\\|.*(")
(paragraph-separate
;; Include action and class lines, start and end of
;; bracketed blocks and end of parenthesized blocks to
;; avoid including these in fill. This isn't ideal.
- "[ \t\f]*$\\|.*#\\|.*[\){}]\\|\\s-*[[:alpha:]_().|!]+:")
+ "[ \t\f]*$\\|.*#\\|.*[){}]\\|\\s-*[[:alpha:]_().|!]+:")
fill-paragraph-function)
(fill-paragraph justify))
t))
t)
(defun cfengine3-indent-line ()
- "Indent a line in Cfengine 3 mode.
+ "Indent a line in CFEngine 3 mode.
Intended as the value of `indent-line-function'."
(let ((pos (- (point-max) (point)))
parse)
(message "%S" parse))
(cond
+ ;; Macros start at 0. But make sure we're not inside a string.
+ ((and (not (nth 3 parse))
+ (looking-at (concat cfengine3-macro-regex)))
+ (indent-line-to 0))
;; Body/bundle blocks start at 0.
((looking-at (concat cfengine3-defuns-regex "\\_>"))
(indent-line-to 0))
(if (> (- (point-max) pos) (point))
(goto-char (- (point-max) pos)))))
+(defun cfengine3-reformat-json-string ()
+ "Reformat the current string as JSON using `json-pretty-print'."
+ (interactive)
+ (let ((ppss (syntax-ppss)))
+ (when (nth 3 ppss) ;inside a string
+ (save-excursion
+ (goto-char (nth 8 ppss))
+ (forward-char 1)
+ (let ((start (point)))
+ (forward-sexp 1)
+ (json-pretty-print start
+ (point)))))))
+
;; CFEngine 3.x grammar
;; specification: blocks
;; CLASS: [.|&!()a-zA-Z0-9_\200-\377]+::
;; CATEGORY: [a-zA-Z_]+:
-(defun cfengine3--current-word (&optional bounds)
- "Propose a word around point in the current CFEngine 3 buffer."
- (let ((c (char-after (point)))
- (s (syntax-ppss)))
- (when (not (nth 3 s)) ; not inside a string
- (if bounds
- (save-excursion
- (let ((oldpoint (point))
- start end)
- (skip-syntax-backward "w_") (setq start (point))
- (goto-char oldpoint)
- (skip-syntax-forward "w_") (setq end (point))
- (when (not (and (eq start oldpoint)
- (eq end oldpoint)))
- (list start (point)))))
- (and c
- (memq (char-syntax c) '(?_ ?w))
- (current-word))))))
-
(defun cfengine3--current-function ()
"Look up current CFEngine 3 function"
- (let* ((syntax (assoc cfengine-cf-promises cfengine-mode-syntax-cache))
- (flist (assoc 'functions syntax)))
+ (let* ((syntax (cfengine3-make-syntax-cache))
+ (flist (assq 'functions syntax)))
(when flist
- (let ((w (cfengine3--current-word)))
+ (let ((w (save-excursion
+ (skip-syntax-forward "w_")
+ (when (search-backward-regexp
+ cfengine-mode-syntax-functions-regex
+ (point-at-bol)
+ t)
+ (match-string 1)))))
(and w (assq (intern w) flist))))))
;; format from "cf-promises -s json", e.g. "sort" function:
"???")
(propertize f 'face 'font-lock-function-name-face)
(mapconcat (lambda (p)
- (let ((type (cdr (assq 'type p)))
+ (let* ((type (cdr (assq 'type p)))
+ (description (cdr (assq 'description p)))
+ (desc-string (if (stringp description)
+ (concat " /" description "/")
+ ""))
(range (cdr (assq 'range p))))
(cond
((not (stringp type)) "???type???")
((not (stringp range)) "???range???")
;; options are lists of possible keywords
((equal type "option")
- (propertize (concat "[" range "]")
+ (propertize (concat "[" range "]" desc-string)
'face
'font-lock-keyword-face))
;; anything else is a type name as a variable
- (t (propertize type
+ (t (propertize (concat type desc-string)
'face
'font-lock-variable-name-face)))))
plist
(if has-some-parameters ", ..." "...")
""))))
+(defun cfengine3-clear-syntax-cache ()
+ "Clear the internal syntax cache.
+Should not be necessary unless you reinstall CFEngine."
+ (interactive)
+ (setq cfengine-mode-syntax-functions-regex nil)
+ (setq cfengine-mode-syntax-cache nil))
+
(defun cfengine3-make-syntax-cache ()
- "Build the CFEngine 3 syntax cache.
-Calls `cfengine-cf-promises' with \"-s json\""
- (when cfengine-cf-promises
- (let ((loaded-json-lib (require 'json nil t))
- (syntax (assoc cfengine-cf-promises cfengine-mode-syntax-cache)))
- (if (not loaded-json-lib)
- (message "JSON library could not be loaded!")
- (unless syntax
- (with-demoted-errors
- (with-temp-buffer
- (call-process-shell-command cfengine-cf-promises
- nil ; no input
- t ; current buffer
- nil ; no redisplay
- "-s" "json")
- (goto-char (point-min))
- (setq syntax (json-read))
- (setq cfengine-mode-syntax-cache
- (cons (cons cfengine-cf-promises syntax)
- cfengine-mode-syntax-cache)))))))))
+ "Build the CFEngine 3 syntax cache and return the syntax.
+Calls `cfengine-cf-promises' with \"-s json\"."
+ (or (cdr (assoc cfengine-cf-promises cfengine-mode-syntax-cache))
+ (let ((syntax (or (when cfengine-cf-promises
+ (with-demoted-errors "cfengine3-make-syntax-cache: %S"
+ (with-temp-buffer
+ (or (zerop (process-file cfengine-cf-promises
+ nil ; no input
+ t ; output
+ nil ; no redisplay
+ "-s" "json"))
+ (error "%s" (buffer-substring
+ (point-min)
+ (progn (goto-char (point-min))
+ (line-end-position)))))
+ (goto-char (point-min))
+ (json-read))))
+ cfengine3-fallback-syntax)))
+ (push (cons cfengine-cf-promises syntax)
+ cfengine-mode-syntax-cache)
+ (setq cfengine-mode-syntax-functions-regex
+ (regexp-opt (mapcar (lambda (def)
+ (format "%s" (car def)))
+ (cdr (assq 'functions syntax)))
+ 'symbols))
+ syntax)))
(defun cfengine3-documentation-function ()
"Document CFengine 3 functions around point.
-Intended as the value of `eldoc-documentation-function', which
-see. Use it by executing `turn-on-eldoc-mode'."
- (cfengine3-make-syntax-cache)
+Intended as the value of `eldoc-documentation-function', which see.
+Use it by enabling `eldoc-mode'."
(let ((fdef (cfengine3--current-function)))
(when fdef
(cfengine3-format-function-docstring fdef))))
(defun cfengine3-completion-function ()
"Return completions for function name around or before point."
- (cfengine3-make-syntax-cache)
- (let* ((bounds (cfengine3--current-word t))
- (syntax (assoc cfengine-cf-promises cfengine-mode-syntax-cache))
- (flist (assoc 'functions syntax)))
+ (let* ((bounds (save-excursion
+ (let ((p (point)))
+ (skip-syntax-backward "w_" (point-at-bol))
+ (list (point) p))))
+ (syntax (cfengine3-make-syntax-cache))
+ (flist (assq 'functions syntax)))
(when bounds
(append bounds (list (cdr flist))))))
("=>" . ?⇒)
("::" . ?∷)))
+(defun cfengine3-create-imenu-index ()
+ "A function for `imenu-create-index-function'.
+Note: defun name is separated by space such as `body
+package_method opencsw' and imenu will replace spaces according
+to `imenu-space-replacement' (which see)."
+ (goto-char (point-min))
+ (let ((defuns ()))
+ (while (re-search-forward cfengine3-defun-full-re nil t)
+ (push (cons (mapconcat #'match-string '(1 2 3) " ")
+ (copy-marker (match-beginning 3)))
+ defuns))
+ (nreverse defuns)))
+
+(defun cfengine3-current-defun ()
+ "A function for `add-log-current-defun-function'."
+ (end-of-line)
+ (beginning-of-defun)
+ (and (looking-at cfengine3-defun-full-re)
+ (mapconcat #'match-string '(1 2 3) " ")))
+
;;;###autoload
(define-derived-mode cfengine3-mode prog-mode "CFE3"
"Major mode for editing CFEngine3 input.
(when buffer-file-name
(shell-quote-argument buffer-file-name)))))
- (set (make-local-variable 'eldoc-documentation-function)
- #'cfengine3-documentation-function)
+ ;; For emacs < 25.1 where `eldoc-documentation-function' defaults to
+ ;; nil.
+ (or eldoc-documentation-function
+ (setq-local eldoc-documentation-function #'ignore))
+ (add-function :before-until (local 'eldoc-documentation-function)
+ #'cfengine3-documentation-function)
(add-hook 'completion-at-point-functions
#'cfengine3-completion-function nil t)
;; Use defuns as the essential syntax block.
- (set (make-local-variable 'beginning-of-defun-function)
- #'cfengine3-beginning-of-defun)
- (set (make-local-variable 'end-of-defun-function)
- #'cfengine3-end-of-defun))
+ (setq-local beginning-of-defun-function #'cfengine3-beginning-of-defun)
+ (setq-local end-of-defun-function #'cfengine3-end-of-defun)
+
+ (setq-local imenu-create-index-function #'cfengine3-create-imenu-index)
+ (setq-local add-log-current-defun-function #'cfengine3-current-defun))
;;;###autoload
(define-derived-mode cfengine2-mode prog-mode "CFE2"
;;;###autoload
(defun cfengine-auto-mode ()
- "Choose between `cfengine2-mode' and `cfengine3-mode' depending
-on the buffer contents"
- (let ((v3 nil))
- (save-restriction
- (goto-char (point-min))
- (while (not (or (eobp) v3))
- (setq v3 (looking-at (concat cfengine3-defuns-regex "\\_>")))
- (forward-line)))
- (if v3 (cfengine3-mode) (cfengine2-mode))))
+ "Choose `cfengine2-mode' or `cfengine3-mode' by buffer contents."
+ (interactive)
+ (if (save-excursion
+ (save-restriction
+ (widen)
+ (goto-char (point-min))
+ (forward-comment (point-max))
+ (or (eobp)
+ (re-search-forward
+ (concat "^\\s-*" cfengine3-defuns-regex "\\_>") nil t))))
+ (cfengine3-mode)
+ (cfengine2-mode)))
(defalias 'cfengine-mode 'cfengine3-mode)