changeset 52888:bc07c51257ae

*** empty log message ***
author Dave Love <fx@gnu.org>
date Mon, 20 Oct 2003 23:16:26 +0000
parents 2a29815ed66e
children 615ebe291578
files lisp/ChangeLog lisp/progmodes/cfengine.el
diffstat 2 files changed, 250 insertions(+), 0 deletions(-) [+]
line wrap: on
line diff
--- a/lisp/ChangeLog	Mon Oct 20 22:44:28 2003 +0000
+++ b/lisp/ChangeLog	Mon Oct 20 23:16:26 2003 +0000
@@ -1,3 +1,7 @@
+2003-10-21  Dave Love  <fx@gnu.org>
+
+	* progmodes/cfengine.el: New file.
+
 2003-10-20  Stefan Monnier  <monnier@iro.umontreal.ca>
 
 	* complete.el (PC-do-completion): Do not forget to use `pred' as the
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/lisp/progmodes/cfengine.el	Mon Oct 20 23:16:26 2003 +0000
@@ -0,0 +1,246 @@
+;;; cfengine.el --- mode for editing Cfengine files
+
+;; Copyright (C) 2003 Free Software Foundation, Inc.
+
+;; Author: Dave Love <fx@gnu.org>
+;; 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 2, 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; see the file COPYING.  If not, write to the
+;; Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+;; Boston, MA 02111-1307, USA.
+
+;;; Commentary:
+
+;; Provides support for editing GNU Cfengine files, including
+;; font-locking, Imenu and indention, but with no special keybindings.
+
+;; Possible customization for auto-mode selection:
+;; (push '(("^cfagent.conf\\'" . cfengine-mode)) auto-mode-alist)
+;; (push '(("^cf\\." . cfengine-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
+;; better fontification and indentation, inter alia.
+
+;;; Code:
+
+(defgroup cfengine ()
+  "Editing Cfengine files."
+  :group 'languages)
+
+(defcustom cfengine-indent 2
+  "*Size of a Cfengine indentation step in columns."
+  :group 'cfengine
+  :type 'integer)
+
+(defcustom cfengine-mode-abbrevs nil
+  "Abbrevs for Cfengine mode."
+  :group 'cfengine
+  :type '(repeat (list (string :tag "Name")
+		       (string :tag "Expansion")
+		       (choice  :tag "Hook" (const nil) function))))
+
+;; Taken from the doc for pre-release 2.1.
+(eval-and-compile
+  (defconst cfengine-actions
+    '("acl" "alerts" "binservers" "broadcast" "control" "classes" "copy"
+      "defaultroute" "disks" "directories" "disable" "editfiles" "files"
+      "filters" "groups" "homeservers" "ignore" "import" "interfaces"
+      "links" "mailserver" "methods" "miscmounts" "mountables"
+      "processes" "packages" "rename" "required" "resolve"
+      "shellcommands" "tidy" "unmount"
+      ;; cfservd
+      "admit" "grant" "deny")
+    "List of the action keywords supported by Cfengine.
+This includes those for cfservd as well as cfagent."))
+
+(defvar cfengine-font-lock-keywords
+  `(;; Actions.
+    ;; List the allowed actions explicitly, so that errors are more obvious.
+    (,(concat "^[ \t]*" (eval-when-compile
+			  (regexp-opt cfengine-actions t))
+	      ":")
+     1 font-lock-keyword-face)
+    ;; Classes.
+    ("^[ \t]*\\([[:alnum:]_().|!]+\\)::" 1 font-lock-function-name-face)
+    ;; Variables.
+    ("$(\\([[: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)
+    ;; File, acl &c in group:   { token ... }
+    ("{[ \t]*\\([^ \t\n]+\\)" 1 font-lock-constant-face)))
+
+(defvar cfengine-imenu-expression
+  `((nil ,(concat "^[ \t]*" (eval-when-compile
+			      (regexp-opt cfengine-actions t))
+		  ":[^:]")
+	 1)
+    ("Variables/classes" "\\<\\([[:alnum:]_]+\\)[ \t]*=[ \t]*(" 1)
+    ("Variables/classes" "\\<define=\\([[:alnum:]_]+\\)" 1)
+    ("Variables/classes" "\\<DefineClass\\>[ \t]+\\([[:alnum:]_]+\\)" 1))
+  "`imenu-generic-expression' for Cfengine mode.")
+
+(defun cfengine-outline-level ()
+  "`outline-level' function for Cfengine mode."
+  (if (looking-at "[^:]+\\(?:[:]+\\)$")
+      (length (match-string 1))))
+
+(defun cfengine-beginning-of-defun ()
+  "`beginning-of-defun' function for Cfengine mode.
+Treats actions as defuns."
+  (end-of-line)
+  (if (re-search-backward "^[[:alpha:]]+: *$" nil t)
+      (beginning-of-line)
+    (goto-char (point-min)))
+  t)
+
+(defun cfengine-end-of-defun ()
+  "`end-of-defun' function for Cfengine mode.
+Treats actions as defuns."
+  (end-of-line)
+  (if (re-search-forward "^[[:alpha:]]+: *$" nil t)
+      (progn (forward-line -1) (end-of-line))
+    (goto-char (point-max)))
+  t)
+
+;; Fixme: Should get an extra indent step in editfiles BeginGroup...s.
+
+(defun cfengine-indent-line ()
+  "Indent a line in Cfengine mode.
+Intended as the value of `indent-line-function'."
+  (let ((pos (- (point-max) (point))))
+    (save-restriction
+      (narrow-to-defun)
+      (back-to-indentation)
+      (cond
+       ;; Action selectors aren't indented; class selectors are
+       ;; indented one step.
+       ((looking-at "[[:alnum:]_().|!]+:\\(:\\)?")
+	(if (match-string 1)
+	    (indent-line-to cfengine-indent)
+	  (indent-line-to 0)))
+       ;; 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 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")
+				     (if (looking-at "[^\n#]")
+					 (current-column)
+				       (skip-chars-backward " \t")
+				       (+ (current-column) -1
+					  cfengine-indent))))
+		   t)
+	  (error nil)))
+       ;; Indent by  two steps after a class selector.
+       ((save-excursion
+	  (re-search-backward "^[ \t]*[[:alnum:]_().|!]+::" nil t))
+	(indent-line-to (* 2 cfengine-indent)))
+       ;; Indent by one step if we're after an action header.
+       ((save-excursion
+	  (goto-char (point-min))
+	  (looking-at "[[:alpha:]]+:[ \t]*$"))
+	(indent-line-to cfengine-indent))
+       ;; 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)))))
+
+;; This doesn't work too well in Emacs 21.2.  See 21.4 development
+;; code.
+(defun cfengine-fill-paragraph (&optional justify)
+  "Fill `paragraphs' in Cfengine code."
+  (interactive "P")
+  (or (if (fboundp 'fill-comment-paragraph)
+	  (fill-comment-paragraph justify) ; post Emacs 21.3
+	;; else do nothing in a comment
+	(nth 4 (parse-partial-sexp (save-excursion
+				     (beginning-of-defun)
+				     (point))
+				   (point))))
+      (let ((paragraph-start
+	     ;; Include start of parenthesized block.
+	     "\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:]_().|!]+:")
+	    fill-paragraph-function)
+	(fill-paragraph justify))
+      t))
+
+;;;###autoload
+(define-derived-mode cfengine-mode fundamental-mode "Cfengine"
+  "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 ?# "<" cfengine-mode-syntax-table)
+  (modify-syntax-entry ?\n ">#" 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 'require-final-newline) t)
+  (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)
+  (set (make-local-variable 'fill-paragraph-function)
+       #'cfengine-fill-paragraph)
+  (define-abbrev-table 'cfengine-mode-abbrev-table cfengine-mode-abbrevs)
+  ;; Fixme: Use `font-lock-syntactic-keywords' to set the args of
+  ;; functions in evaluated classes to string syntax, and then obey
+  ;; syntax properties.
+  (setq font-lock-defaults
+	'(cfengine-font-lock-keywords nil nil nil beginning-of-line))
+  (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))
+
+(provide 'cfengine)
+
+;;; cfengine.el ends here