Commit eee8207a authored by Ted Zlatanov's avatar Ted Zlatanov
Browse files

Moved `cfengine3-mode' to cfengine.el. Removed cfengine3.el.

* progmodes/cfengine.el: Moved all cfengine3.el functionality
here.  Noted Ted Zlatanov as the maintainer.
(cfengine-common-settings, cfengine-common-syntax): New functions
to set up common things between `cfengine-mode' and
`cfengine3-mode'.
(cfengine3-mode): New mode.
(cfengine3-defuns cfengine3-defuns-regex
(cfengine3-class-selector-regex cfengine3-category-regex)
(cfengine3-vartypes cfengine3-font-lock-keywords)
(cfengine3-beginning-of-defun, cfengine3-end-of-defun)
(cfengine3-indent-line): Added from cfengine3.el.
parent ca5eed61
2011-07-01 Teodor Zlatanov <tzz@lifelogs.com>
* progmodes/cfengine.el: Moved all cfengine3.el functionality
here. Noted Ted Zlatanov as the maintainer.
(cfengine-common-settings, cfengine-common-syntax): New functions
to set up common things between `cfengine-mode' and
`cfengine3-mode'.
(cfengine3-mode): New mode.
(cfengine3-defuns cfengine3-defuns-regex
(cfengine3-class-selector-regex cfengine3-category-regex)
(cfengine3-vartypes cfengine3-font-lock-keywords)
(cfengine3-beginning-of-defun, cfengine3-end-of-defun)
(cfengine3-indent-line): Added from cfengine3.el.
2011-07-01 Michael Albinus <michael.albinus@gmx.de>
* net/tramp.el (tramp-encoding-command-interactive): New defcustom.
......
......@@ -3,6 +3,7 @@
;; Copyright (C) 2001-2011 Free Software Foundation, Inc.
;; Author: Dave Love <fx@gnu.org>
;; Maintainer: Ted Zlatanov <tzz@lifelogs.com>
;; Keywords: languages
;; This file is part of GNU Emacs.
......@@ -28,6 +29,7 @@
;; Possible customization for auto-mode selection:
;; (push '(("^cfagent.conf\\'" . cfengine-mode)) auto-mode-alist)
;; (push '(("^cf\\." . cfengine-mode)) auto-mode-alist)
;; (push '(("\\.cf\\'" . cfengine3-mode)) auto-mode-alist)
;; This is not the same as the mode written by Rolf Ebert
;; <ebert@waporo.muc.de>, distributed with cfengine-2.0.5. It does
......@@ -63,7 +65,27 @@
;; cfservd
"admit" "grant" "deny")
"List of the action keywords supported by Cfengine.
This includes those for cfservd as well as cfagent."))
This includes those for cfservd as well as cfagent.")
(defconst cfengine3-defuns
(mapcar
'symbol-name
'(bundle body))
"List of the CFEngine 3.x defun headings.")
(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-category-regex "\\([[:alnum:]_]+\\):")
(defconst cfengine3-vartypes
(mapcar
'symbol-name
'(string int real slist ilist rlist irange rrange counter))
"List of the CFEngine 3.x variable types."))
(defvar cfengine-font-lock-keywords
`(;; Actions.
......@@ -82,6 +104,31 @@ This includes those for cfservd as well as cfagent."))
;; File, acl &c in group: { token ... }
("{[ \t]*\\([^ \t\n]+\\)" 1 font-lock-constant-face)))
(defvar cfengine3-font-lock-keywords
`(
(,(concat "^[ \t]*" cfengine3-class-selector-regex)
1 font-lock-keyword-face)
(,(concat "^[ \t]*" cfengine3-category-regex)
1 font-lock-builtin-face)
;; Variables, including scope, e.g. module.var
("[@$](\\([[:alnum:]_.]+\\))" 1 font-lock-variable-name-face)
("[@$]{\\([[:alnum:]_.]+\\)}" 1 font-lock-variable-name-face)
;; Variable definitions.
("\\<\\([[:alnum:]_]+\\)[ \t]*=[ \t]*(" 1 font-lock-variable-name-face)
;; CFEngine 3.x faces
;; defuns
(,(concat "\\<" cfengine3-defuns-regex "\\>"
"[ \t]+\\<\\([[:alnum:]_]+\\)\\>"
"[ \t]+\\<\\([[:alnum:]_]+\\)\\((\\([^)]*\\))\\)?")
(1 font-lock-builtin-face)
(2 font-lock-constant-name-face)
(3 font-lock-function-name-face)
(5 font-lock-variable-name-face))
;; variable types
(,(concat "\\<" (eval-when-compile (regexp-opt cfengine3-vartypes t)) "\\>")
1 font-lock-type-face)))
(defvar cfengine-imenu-expression
`((nil ,(concat "^[ \t]*" (eval-when-compile
(regexp-opt cfengine-actions t))
......@@ -197,6 +244,191 @@ Intended as the value of `indent-line-function'."
(fill-paragraph justify))
t))
(defun cfengine3-beginning-of-defun ()
"`beginning-of-defun' function for Cfengine 3 mode.
Treats body/bundle blocks as defuns."
(unless (<= (current-column) (current-indentation))
(end-of-line))
(if (re-search-backward (concat "^[ \t]*" cfengine3-defuns-regex "\\>") nil t)
(beginning-of-line)
(goto-char (point-min)))
t)
(defun cfengine3-end-of-defun ()
"`end-of-defun' function for Cfengine 3 mode.
Treats body/bundle blocks as defuns."
(end-of-line)
(if (re-search-forward (concat "^[ \t]*" cfengine3-defuns-regex "\\>") nil t)
(beginning-of-line)
(goto-char (point-max)))
t)
(defun cfengine3-indent-line ()
"Indent a line in Cfengine 3 mode.
Intended as the value of `indent-line-function'."
(let ((pos (- (point-max) (point)))
parse)
(save-restriction
(narrow-to-defun)
(back-to-indentation)
(setq parse (parse-partial-sexp (point-min) (point)))
(message "%S" parse)
(cond
;; body/bundle blocks start at 0
((looking-at (concat cfengine3-defuns-regex "\\>"))
(indent-line-to 0))
;; categories are indented one step
((looking-at (concat cfengine3-category-regex "[ \t]*$"))
(indent-line-to cfengine-indent))
;; class selectors are indented two steps
((looking-at (concat cfengine3-class-selector-regex "[ \t]*$"))
(indent-line-to (* 2 cfengine-indent)))
;; Outdent leading close brackets one step.
((or (eq ?\} (char-after))
(eq ?\) (char-after)))
(condition-case ()
(indent-line-to (save-excursion
(forward-char)
(backward-sexp)
(current-column)))
(error nil)))
;; inside a string and it starts before this line
((and (nth 3 parse)
(< (nth 8 parse) (save-excursion (beginning-of-line) (point))))
(indent-line-to 0))
;; inside a defun, but not a nested list (depth is 1)
((= 1 (nth 0 parse))
(indent-line-to (* (+ 2 (nth 0 parse)) cfengine-indent)))
;; Inside brackets/parens: indent to start column of non-comment
;; token on line following open bracket or by one step from open
;; bracket's column.
((condition-case ()
(progn (indent-line-to (save-excursion
(backward-up-list)
(forward-char)
(skip-chars-forward " \t")
(cond
((looking-at "[^\n#]")
(current-column))
((looking-at "[^\n#]")
(current-column))
(t
(skip-chars-backward " \t")
(+ (current-column) -1
cfengine-indent)))))
t)
(error nil)))
;; Else don't indent.
(t (indent-line-to 0))))
;; If initial point was within line's indentation,
;; position after the indentation. Else stay at same point in text.
(if (> (- (point-max) pos) (point))
(goto-char (- (point-max) pos)))))
;; CFEngine 3.x grammar
;; specification: blocks
;; blocks: block | blocks block;
;; block: bundle typeid blockid bundlebody
;; | bundle typeid blockid usearglist bundlebody
;; | body typeid blockid bodybody
;; | body typeid blockid usearglist bodybody;
;; typeid: id
;; blockid: id
;; usearglist: '(' aitems ')';
;; aitems: aitem | aitem ',' aitems |;
;; aitem: id
;; bundlebody: '{' statements '}'
;; statements: statement | statements statement;
;; statement: category | classpromises;
;; bodybody: '{' bodyattribs '}'
;; bodyattribs: bodyattrib | bodyattribs bodyattrib;
;; bodyattrib: class | selections;
;; selections: selection | selections selection;
;; selection: id ASSIGN rval ';' ;
;; classpromises: classpromise | classpromises classpromise;
;; classpromise: class | promises;
;; promises: promise | promises promise;
;; category: CATEGORY
;; promise: promiser ARROW rval constraints ';' | promiser constraints ';';
;; constraints: constraint | constraints ',' constraint |;
;; constraint: id ASSIGN rval;
;; class: CLASS
;; id: ID
;; rval: ID | QSTRING | NAKEDVAR | list | usefunction
;; list: '{' litems '}' ;
;; litems: litem | litem ',' litems |;
;; litem: ID | QSTRING | NAKEDVAR | list | usefunction
;; functionid: ID | NAKEDVAR
;; promiser: QSTRING
;; usefunction: functionid givearglist
;; givearglist: '(' gaitems ')'
;; gaitems: gaitem | gaitems ',' gaitem |;
;; gaitem: ID | QSTRING | NAKEDVAR | list | usefunction
;; # from lexer:
;; bundle: "bundle"
;; body: "body"
;; COMMENT #[^\n]*
;; NAKEDVAR [$@][(][a-zA-Z0-9_\200-\377.]+[)]|[$@][{][a-zA-Z0-9_\200-\377.]+[}]
;; ID: [a-zA-Z0-9_\200-\377]+
;; ASSIGN: "=>"
;; ARROW: "->"
;; QSTRING: \"((\\\")|[^"])*\"|\'((\\\')|[^'])*\'|`[^`]*`
;; CLASS: [.|&!()a-zA-Z0-9_\200-\377]+::
;; CATEGORY: [a-zA-Z_]+:
(defun cfengine-common-settings ()
(set (make-local-variable 'syntax-propertize-function)
;; In the main syntax-table, \ is marked as a punctuation, because
;; of its use in DOS-style directory separators. Here we try to
;; recognize the cases where \ is used as an escape inside strings.
(syntax-propertize-rules ("\\(\\(?:\\\\\\)+\\)\"" (1 "\\"))))
(set (make-local-variable 'parens-require-spaces) nil)
(set (make-local-variable 'comment-start) "# ")
(set (make-local-variable 'comment-start-skip)
"\\(\\(?:^\\|[^\\\\\n]\\)\\(?:\\\\\\\\\\)*\\)#+[ \t]*")
;; Like Lisp mode. Without this, we lose with, say,
;; `backward-up-list' when there's an unbalanced quote in a
;; preceding comment.
(set (make-local-variable 'parse-sexp-ignore-comments) t))
(defun cfengine-common-syntax (table)
;; the syntax defaults seem OK to give reasonable word movement
(modify-syntax-entry ?# "<" table)
(modify-syntax-entry ?\n ">#" table)
(modify-syntax-entry ?\" "\"" table)
;; variable substitution:
(modify-syntax-entry ?$ "." table)
;; Doze path separators:
(modify-syntax-entry ?\\ "." table))
;;;###autoload
(define-derived-mode cfengine3-mode prog-mode "CFEngine3"
"Major mode for editing cfengine input.
There are no special keybindings by default.
Action blocks are treated as defuns, i.e. \\[beginning-of-defun] moves
to the action header."
(cfengine-common-settings)
(cfengine-common-syntax cfengine3-mode-syntax-table)
(set (make-local-variable 'indent-line-function) #'cfengine3-indent-line)
(setq font-lock-defaults
'(cfengine3-font-lock-keywords nil nil nil beginning-of-defun))
;; 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))
;;;###autoload
(define-derived-mode cfengine-mode prog-mode "Cfengine"
"Major mode for editing cfengine input.
......@@ -204,25 +436,15 @@ There are no special keybindings by default.
Action blocks are treated as defuns, i.e. \\[beginning-of-defun] moves
to the action header."
(modify-syntax-entry ?# "<" cfengine-mode-syntax-table)
(modify-syntax-entry ?\n ">#" cfengine-mode-syntax-table)
(cfengine-common-settings)
(cfengine-common-syntax cfengine-mode-syntax-table)
;; Shell commands can be quoted by single, double or back quotes.
;; It's debatable whether we should define string syntax, but it
;; should avoid potential confusion in some cases.
(modify-syntax-entry ?\" "\"" cfengine-mode-syntax-table)
(modify-syntax-entry ?\' "\"" cfengine-mode-syntax-table)
(modify-syntax-entry ?\` "\"" cfengine-mode-syntax-table)
;; variable substitution:
(modify-syntax-entry ?$ "." cfengine-mode-syntax-table)
;; Doze path separators:
(modify-syntax-entry ?\\ "." cfengine-mode-syntax-table)
;; Otherwise, syntax defaults seem OK to give reasonable word
;; movement.
(set (make-local-variable 'parens-require-spaces) nil)
(set (make-local-variable 'comment-start) "# ")
(set (make-local-variable 'comment-start-skip)
"\\(\\(?:^\\|[^\\\\\n]\\)\\(?:\\\\\\\\\\)*\\)#+[ \t]*")
(set (make-local-variable 'indent-line-function) #'cfengine-indent-line)
(set (make-local-variable 'outline-regexp) "[ \t]*\\(\\sw\\|\\s_\\)+:+")
(set (make-local-variable 'outline-level) #'cfengine-outline-level)
......@@ -233,20 +455,12 @@ to the action header."
'(cfengine-font-lock-keywords nil nil nil beginning-of-line))
;; Fixme: set the args of functions in evaluated classes to string
;; syntax, and then obey syntax properties.
(set (make-local-variable 'syntax-propertize-function)
;; In the main syntax-table, \ is marked as a punctuation, because
;; of its use in DOS-style directory separators. Here we try to
;; recognize the cases where \ is used as an escape inside strings.
(syntax-propertize-rules ("\\(\\(?:\\\\\\)+\\)\"" (1 "\\"))))
(setq imenu-generic-expression cfengine-imenu-expression)
(set (make-local-variable 'beginning-of-defun-function)
#'cfengine-beginning-of-defun)
(set (make-local-variable 'end-of-defun-function) #'cfengine-end-of-defun)
;; Like Lisp mode. Without this, we lose with, say,
;; `backward-up-list' when there's an unbalanced quote in a
;; preceding comment.
(set (make-local-variable 'parse-sexp-ignore-comments) t))
(set (make-local-variable 'end-of-defun-function) #'cfengine-end-of-defun))
(provide 'cfengine3)
(provide 'cfengine)
;;; cfengine.el ends here
;;; cfengine3.el --- mode for editing Cfengine 3 files
;; Copyright (C) 2001-2011 Free Software Foundation, Inc.
;; Author: Ted Zlatanov <tzz@lifelogs.com>
;; Keywords: languages
;; This file is part of GNU Emacs.
;; GNU Emacs is free software: you can redistribute it and/or modify
;; it under the terms of the GNU General Public License as published by
;; the Free Software Foundation, either version 3 of the License, or
;; (at your option) any later version.
;; GNU Emacs is distributed in the hope that it will be useful,
;; but WITHOUT ANY WARRANTY; without even the implied warranty of
;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
;; GNU General Public License for more details.
;; You should have received a copy of the GNU General Public License
;; along with GNU Emacs. If not, see <http://www.gnu.org/licenses/>.
;;; Commentary:
;; Supports only cfengine 3, unlike the older cfengine.el which
;; supports 1.x and 2.x.
;; Possible customization for auto-mode selection:
;; (push '(("^cfagent.conf\\'" . cfengine3-mode)) auto-mode-alist)
;; (push '(("^cf\\." . cfengine3-mode)) auto-mode-alist)
;; (push '(("\\.cf\\'" . cfengine3-mode)) auto-mode-alist)
;;; Code:
(defgroup cfengine3 ()
"Editing CFEngine 3 files."
:group 'languages)
(defcustom cfengine3-indent 2
"*Size of a CFEngine 3 indentation step in columns."
:group 'cfengine3
:type 'integer)
(eval-and-compile
(defconst cfengine3-defuns
(mapcar
'symbol-name
'(bundle body))
"List of the CFEngine 3.x defun headings.")
(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-category-regex "\\([[:alnum:]_]+\\):")
(defconst cfengine3-vartypes
(mapcar
'symbol-name
'(string int real slist ilist rlist irange rrange counter))
"List of the CFEngine 3.x variable types."))
(defvar cfengine3-font-lock-keywords
`(
(,(concat "^[ \t]*" cfengine3-class-selector-regex)
1 font-lock-keyword-face)
(,(concat "^[ \t]*" cfengine3-category-regex)
1 font-lock-builtin-face)
;; Variables, including scope, e.g. module.var
("[@$](\\([[:alnum:]_.]+\\))" 1 font-lock-variable-name-face)
("[@$]{\\([[:alnum:]_.]+\\)}" 1 font-lock-variable-name-face)
;; Variable definitions.
("\\<\\([[:alnum:]_]+\\)[ \t]*=[ \t]*(" 1 font-lock-variable-name-face)
;; CFEngine 3.x faces
;; defuns
(,(concat "\\<" cfengine3-defuns-regex "\\>"
"[ \t]+\\<\\([[:alnum:]_]+\\)\\>"
"[ \t]+\\<\\([[:alnum:]_]+\\)\\((\\([^)]*\\))\\)?")
(1 font-lock-builtin-face)
(2 font-lock-constant-name-face)
(3 font-lock-function-name-face)
(5 font-lock-variable-name-face))
;; variable types
(,(concat "\\<" (eval-when-compile (regexp-opt cfengine3-vartypes t)) "\\>")
1 font-lock-type-face)))
(defun cfengine3-beginning-of-defun ()
"`beginning-of-defun' function for Cfengine 3 mode.
Treats body/bundle blocks as defuns."
(unless (<= (current-column) (current-indentation))
(end-of-line))
(if (re-search-backward (concat "^[ \t]*" cfengine3-defuns-regex "\\>") nil t)
(beginning-of-line)
(goto-char (point-min)))
t)
(defun cfengine3-end-of-defun ()
"`end-of-defun' function for Cfengine 3 mode.
Treats body/bundle blocks as defuns."
(end-of-line)
(if (re-search-forward (concat "^[ \t]*" cfengine3-defuns-regex "\\>") nil t)
(beginning-of-line)
(goto-char (point-max)))
t)
(defun cfengine3-indent-line ()
"Indent a line in Cfengine mode.
Intended as the value of `indent-line-function'."
(let ((pos (- (point-max) (point)))
parse)
(save-restriction
(narrow-to-defun)
(back-to-indentation)
(setq parse (parse-partial-sexp (point-min) (point)))
(message "%S" parse)
(cond
;; body/bundle blocks start at 0
((looking-at (concat cfengine3-defuns-regex "\\>"))
(indent-line-to 0))
;; categories are indented one step
((looking-at (concat cfengine3-category-regex "[ \t]*$"))
(indent-line-to cfengine3-indent))
;; class selectors are indented two steps
((looking-at (concat cfengine3-class-selector-regex "[ \t]*$"))
(indent-line-to (* 2 cfengine3-indent)))
;; Outdent leading close brackets one step.
((or (eq ?\} (char-after))
(eq ?\) (char-after)))
(condition-case ()
(indent-line-to (save-excursion
(forward-char)
(backward-sexp)
(current-column)))
(error nil)))
;; inside a string and it starts before this line
((and (nth 3 parse)
(< (nth 8 parse) (save-excursion (beginning-of-line) (point))))
(indent-line-to 0))
;; inside a defun, but not a nested list (depth is 1)
((= 1 (nth 0 parse))
(indent-line-to (* (+ 2 (nth 0 parse)) cfengine3-indent)))
;; Inside brackets/parens: indent to start column of non-comment
;; token on line following open bracket or by one step from open
;; bracket's column.
((condition-case ()
(progn (indent-line-to (save-excursion
(backward-up-list)
(forward-char)
(skip-chars-forward " \t")
(cond
((looking-at "[^\n#]")
(current-column))
((looking-at "[^\n#]")
(current-column))
(t
(skip-chars-backward " \t")
(+ (current-column) -1
cfengine3-indent)))))
t)
(error nil)))
;; Else don't indent.
(t (indent-line-to 0))))
;; If initial point was within line's indentation,
;; position after the indentation. Else stay at same point in text.
(if (> (- (point-max) pos) (point))
(goto-char (- (point-max) pos)))))
;; (defvar cfengine3-smie-grammar
;; (smie-prec2->grammar
;; (smie-merge-prec2s
;; (smie-bnf->prec2
;; '((token)
;; (decls (decls "body" decls)
;; (decls "bundle" decls))
;; (insts (token ":" insts)))
;; '((assoc "body" "bundle")))
;; (smie-precs->prec2
;; '((right ":")
;; (right "::")
;; (assoc ";")
;; (assoc ",")
;; (right "=>"))))))
;; (defun cfengine3-smie-rules (kind token)
;; (pcase (cons kind token)
;; (`(:elem . basic) 2)
;; (`(:list-intro . ,(or `"body" `"bundle")) t)
;; (`(:after . ":") 2)
;; (`(:after . "::") 2)))
;; (defun cfengine3-show-all-tokens ()
;; (interactive)
;; (goto-char (point-min))
;; (while (not (eobp))
;; (let* ((p (point))
;; (token (funcall smie-forward-token-function)))
;; (delete-region p (point))
;; (insert-before-markers token)
;; (forward-char))))
;; (defun cfengine3-line-classes ()
;; (interactive)
;; (save-excursion
;; (beginning-of-line)
;; (let* ((todo (buffer-substring (point)
;; (save-excursion (end-of-line) (point))))
;; (original (concat (loop for c across todo
;; collect (char-syntax c)))))
;; (format "%s\n%s" original todo))))
;; (defun cfengine3-show-all-classes ()
;; (interactive)
;; (goto-char (point-min))
;; (while (not (eobp))
;; (let ((repl (cfengine3-line-classes)))
;; (kill-line)
;; (insert repl)
;; (insert "\n"))))
;; specification: blocks
;; blocks: block | blocks block;
;; block: bundle typeid blockid bundlebody
;; | bundle typeid blockid usearglist bundlebody
;; | body typeid blockid bodybody
;; | body typeid blockid usearglist bodybody;
;; typeid: id
;; blockid: id
;; usearglist: '(' aitems ')';
;; aitems: aitem | aitem ',' aitems |;
;; aitem: id
;; bundlebody: '{' statements '}'
;; statements: statement | statements statement;
;; statement: category | classpromises;
;; bodybody: '{' bodyattribs '}'
;; bodyattribs: bodyattrib | bodyattribs bodyattrib;
;; bodyattrib: class | selections;
;; selections: selection | selections selection;
;; selection: id ASSIGN rval ';' ;
;; classpromises: classpromise | classpromises classpromise;
;; classpromise: class | promises;
;; promises: promise | promises promise;
;; category: CATEGORY
;; promise: promiser ARROW rval constraints ';' | promiser constraints ';';
;; constraints: constraint | constraints ',' constraint |;
;; constraint: id ASSIGN rval;
;; class: CLASS
;; id: ID
;; rval: ID | QSTRING | NAKEDVAR | list | usefunction
;; list: '{' litems '}' ;
;; litems: litem | litem ',' litems |;
;; litem: ID | QSTRING | NAKEDVAR | list | usefunction
;; functionid: ID | NAKEDVAR
;; promiser: QSTRING
;; usefunction: functionid givearglist
;; givearglist: '(' gaitems ')'
;; gaitems: gaitem | gaitems ',' gaitem |;
;; gaitem: ID | QSTRING | NAKEDVAR | list | usefunction
;; # from lexer:
;; bundle: "bundle"
;; body: "body"
;; COMMENT #[^\n]*
;; NAKEDVAR [$@][(][a-zA-Z0-9_\200-\377.]+[)]|[$@][{][a-zA-Z0-9_\200-\377.]+[}]
;; ID: [a-zA-Z0-9_\200-\377]+
;; ASSIGN: "=>"
;; ARROW: "->"
;; QSTRING: \"((\\\")|[^"])*\"|\'((\\\')|[^'])*\'|`[^`]*`
;; CLASS: [.|&!()a-zA-Z0-9_\200-\377]+::
;; CATEGORY: [a-zA-Z_]+:
;;;###autoload
(define-derived-mode cfengine3-mode prog-mode "CFEngine3"
"Major mode for editing cfengine input.
There are no special keybindings by default.
Action blocks are treated as defuns, i.e. \\[beginning-of-defun] moves
to the action header."
(modify-syntax-entry ?# "<" cfengine3-mode-syntax-table)
(modify-syntax-entry ?\n ">#" cfengine3-mode-syntax-table)
(modify-syntax-entry ?\" "\"" cfengine3-mode-syntax-table)
;; variable substitution:
(modify-syntax-entry ?$ "." cf