changeset 61521:2f8b7bd746aa

Move here from parent dir.
author Lute Kamstra <lute@gnu.org>
date Wed, 13 Apr 2005 08:55:01 +0000
parents edf4e619593c
children a5836c00c2ae
files lisp/emacs-lisp/generic.el
diffstat 1 files changed, 345 insertions(+), 0 deletions(-) [+]
line wrap: on
line diff
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/lisp/emacs-lisp/generic.el	Wed Apr 13 08:55:01 2005 +0000
@@ -0,0 +1,345 @@
+;;; generic.el --- defining simple major modes with comment and font-lock
+;;
+;; Copyright (C) 1997, 1999, 2004, 2005 Free Software Foundation, Inc.
+;;
+;; Author:  Peter Breton <pbreton@cs.umb.edu>
+;; Created: Fri Sep 27 1996
+;; Keywords: generic, comment, font-lock
+
+;; 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:
+
+;; INTRODUCTION:
+;;
+;; The macro `define-generic-mode' can be used to define small modes
+;; which provide basic comment and font-lock support.  These modes are
+;; intended for the many configuration files and such which are too
+;; small for a "real" mode, but still have a regular syntax, comment
+;; characters and the like.
+;;
+;; Each generic mode can define the following:
+;;
+;; * List of comment-characters.  The entries in this list should be
+;;   either a character, a one or two character string or a cons pair.
+;;   If the entry is a character or a string, it is added to the
+;;   mode's syntax table with `comment-start' syntax.  If the entry is
+;;   a cons pair, the elements of the pair are considered to be
+;;   `comment-start' and `comment-end' respectively.  (The latter
+;;   should be nil if you want comments to end at end of line.)
+;;   LIMITATIONS: Emacs does not support comment strings of more than
+;;   two characters in length.
+;;
+;; * List of keywords to font-lock.  Each keyword should be a string.
+;;   If you have additional keywords which should be highlighted in a
+;;   face different from `font-lock-keyword-face', you can use the
+;;   convenience function `generic-make-keywords-list' (which see),
+;;   and add the result to the following list:
+;;
+;; * Additional expressions to font-lock.  This should be a list of
+;;   expressions, each of which should be of the same form as those in
+;;   `font-lock-keywords'.
+;;
+;; * List of regular expressions to be placed in auto-mode-alist.
+;;
+;; * List of functions to call to do some additional setup
+;;
+;; This should pretty much cover basic functionality; if you need much
+;; more than this, or you find yourself writing extensive customizations,
+;; perhaps you should be writing a major mode instead!
+;;
+;; EXAMPLE:
+;;
+;; You can use `define-generic-mode' like this:
+;;
+;;   (define-generic-mode 'foo-generic-mode
+;;     (list ?%)
+;;     (list "keyword")
+;;     nil
+;;     (list "\\.FOO\\'")
+;;     (list 'foo-setup-function))
+;;
+;; to define a new generic-mode `foo-generic-mode', which has '%' as a
+;; comment character, and "keyword" as a keyword.  When files which
+;; end in '.FOO' are loaded, Emacs will go into foo-generic-mode and
+;; call foo-setup-function.  You can also use the function
+;; `foo-generic-mode' (which is interactive) to put a buffer into
+;; foo-generic-mode.
+;;
+;; GOTCHAS:
+;;
+;; Be careful that your font-lock definitions are correct.  Getting
+;; them wrong can cause Emacs to continually attempt to fontify! This
+;; problem is not specific to generic-mode.
+
+;; Credit for suggestions, brainstorming, help with debugging:
+;;   ACorreir@pervasive-sw.com (Alfred Correira)
+;; Extensive cleanup by:
+;;   Stefan Monnier (monnier+gnu/emacs@flint.cs.yale.edu)
+
+;;; Code:
+
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+;; Internal Variables
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+
+(defvar generic-font-lock-keywords nil
+  "Keywords for `font-lock-defaults' in a generic mode.")
+(make-variable-buffer-local 'generic-font-lock-keywords)
+(defvaralias 'generic-font-lock-defaults 'generic-font-lock-keywords)
+(make-obsolete-variable 'generic-font-lock-defaults 'generic-font-lock-keywords "22.1")
+
+;;;###autoload
+(defvar generic-mode-list nil
+  "A list of mode names for `generic-mode'.
+Do not add entries to this list directly; use `define-generic-mode'
+instead (which see).")
+
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+;; Functions
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+
+;;;###autoload
+(defmacro define-generic-mode (mode comment-list keyword-list
+				    font-lock-list auto-mode-list
+				    function-list &optional docstring
+				    &rest custom-keyword-args)
+  "Create a new generic mode MODE.
+
+MODE is the name of the command for the generic mode; it need not
+be quoted.  The optional DOCSTRING is the documentation for the
+mode command.  If you do not supply it, a default documentation
+string will be used instead.
+
+COMMENT-LIST is a list, whose entries are either a single
+character, a one or two character string or a cons pair.  If the
+entry is a character or a string, it is added to the mode's
+syntax table with `comment-start' syntax.  If the entry is a cons
+pair, the elements of the pair are considered to be
+`comment-start' and `comment-end' respectively.  (The latter
+should be nil if you want comments to end at end of line.)  Note
+that Emacs has limitations regarding comment characters.
+
+KEYWORD-LIST is a list of keywords to highlight with
+`font-lock-keyword-face'.  Each keyword should be a string.
+
+FONT-LOCK-LIST is a list of additional expressions to highlight.
+Each entry in the list should have the same form as an entry in
+`font-lock-keywords'.
+
+AUTO-MODE-LIST is a list of regular expressions to add to
+`auto-mode-alist'.  These regexps are added to `auto-mode-alist'
+as soon as `define-generic-mode' is called.
+
+FUNCTION-LIST is a list of functions to call to do some
+additional setup.
+
+The optional CUSTOM-KEYWORD-ARGS are pairs of keywords and
+values.  They will be passed to the generated `defcustom' form of
+the mode hook variable MODE-hook.  Defaults to MODE without the
+possible trailing \"-mode\".  (This default may not be a valid
+customization group defined with `defgroup'.  Make sure it is.)
+You can specify keyword arguments without specifying a docstring.
+
+See the file generic-x.el for some examples of `define-generic-mode'."
+  (declare (debug (sexp def-form def-form def-form form def-form
+			[&optional stringp] &rest [keywordp form]))
+	   (indent 1))
+
+  ;; Backward compatibility.
+  (when (eq (car-safe mode) 'quote)
+    (setq mode (eval mode)))
+
+  (when (and docstring (not (stringp docstring)))
+    ;; DOCSTRING is not a string so we assume that it's actually the
+    ;; first keyword of CUSTOM-KEYWORD-ARGS.
+    (push docstring custom-keyword-args)
+    (setq docstring nil))
+
+  (let* ((name (symbol-name mode))
+	 (pretty-name (capitalize (replace-regexp-in-string
+				   "-mode\\'" "" name)))
+	 (mode-hook (intern (concat name "-hook"))))
+
+    (unless (plist-get custom-keyword-args :group)
+      (setq custom-keyword-args
+	    (plist-put custom-keyword-args 
+		       :group `',(intern (replace-regexp-in-string
+					  "-mode\\'" "" name)))))
+
+    `(progn
+       ;; Add a new entry.
+       (add-to-list 'generic-mode-list ,name)
+
+       ;; Add it to auto-mode-alist
+       (dolist (re ,auto-mode-list)
+	 (add-to-list 'auto-mode-alist (cons re ',mode)))
+
+       (defcustom ,mode-hook nil
+	 ,(concat "Hook run when entering " pretty-name " mode.")
+	 :type 'hook
+	 ,@custom-keyword-args)
+
+       (defun ,mode ()
+	 ,(or docstring
+	      (concat pretty-name " mode.\n"
+		      "This a generic mode defined with `define-generic-mode'."))
+	 (interactive)
+	 (generic-mode-internal ',mode ,comment-list ,keyword-list
+				,font-lock-list ,function-list)))))
+
+;;;###autoload
+(defun generic-mode-internal (mode comment-list keyword-list
+				   font-lock-list function-list)
+  "Go into the generic mode MODE."
+  (let* ((name (symbol-name mode))
+	 (pretty-name (capitalize (replace-regexp-in-string
+				   "-mode\\'" "" name)))
+	 (mode-hook (intern (concat name "-hook"))))
+
+    (kill-all-local-variables)
+
+    (setq major-mode mode
+	  mode-name pretty-name)
+
+    (generic-mode-set-comments comment-list)
+
+    ;; Font-lock functionality.
+    ;; Font-lock-defaults is always set even if there are no keywords
+    ;; or font-lock expressions, so comments can be highlighted.
+    (setq generic-font-lock-keywords font-lock-list)
+    (when keyword-list
+      (push (concat "\\_<" (regexp-opt keyword-list t) "\\_>")
+	    generic-font-lock-keywords))
+    (setq font-lock-defaults '(generic-font-lock-keywords nil))
+
+    ;; Call a list of functions
+    (mapcar 'funcall function-list)
+
+    (run-mode-hooks mode-hook)))
+
+;;;###autoload
+(defun generic-mode (mode)
+  "Enter generic mode MODE.
+
+Generic modes provide basic comment and font-lock functionality
+for \"generic\" files.  (Files which are too small to warrant their
+own mode, but have comment characters, keywords, and the like.)
+
+To define a generic-mode, use the function `define-generic-mode'.
+Some generic modes are defined in `generic-x.el'."
+  (interactive
+   (list (completing-read "Generic mode: " generic-mode-list nil t)))
+  (funcall (intern mode)))
+
+;;; Comment Functionality
+(defun generic-mode-set-comments (comment-list)
+  "Set up comment functionality for generic mode."
+  (let ((st (make-syntax-table))
+	(chars nil)
+	(comstyles))
+    (make-local-variable 'comment-start)
+    (make-local-variable 'comment-start-skip)
+    (make-local-variable 'comment-end)
+
+    ;; Go through all the comments
+    (dolist (start comment-list)
+      (let (end (comstyle ""))
+	;; Normalize
+	(when (consp start)
+	  (setq end (cdr start))
+	  (setq start (car start)))
+	(when (char-valid-p start) (setq start (char-to-string start)))
+	(cond
+	 ((char-valid-p end)   (setq end (char-to-string end)))
+	 ((zerop (length end)) (setq end "\n")))
+
+	;; Setup the vars for `comment-region'
+	(if comment-start
+	    ;; We have already setup a comment-style, so use style b
+	    (progn
+	      (setq comstyle "b")
+	      (setq comment-start-skip
+		    (concat comment-start-skip "\\|" (regexp-quote start) "+\\s-*")))
+	  ;; First comment-style
+	  (setq comment-start start)
+	  (setq comment-end (if (string-equal end "\n") "" end))
+	  (setq comment-start-skip (concat (regexp-quote start) "+\\s-*")))
+
+       ;; Reuse comstyles if necessary
+       (setq comstyle
+             (or (cdr (assoc start comstyles))
+                 (cdr (assoc end comstyles))
+                 comstyle))
+       (push (cons start comstyle) comstyles)
+       (push (cons end comstyle) comstyles)
+
+	;; Setup the syntax table
+	(if (= (length start) 1)
+	    (modify-syntax-entry (string-to-char start)
+				 (concat "< " comstyle) st)
+	  (let ((c0 (elt start 0)) (c1 (elt start 1)))
+	    ;; Store the relevant info but don't update yet
+	    (push (cons c0 (concat (cdr (assoc c0 chars)) "1")) chars)
+	    (push (cons c1 (concat (cdr (assoc c1 chars))
+				   (concat "2" comstyle))) chars)))
+	(if (= (length end) 1)
+	    (modify-syntax-entry (string-to-char end)
+				 (concat ">" comstyle) st)
+	  (let ((c0 (elt end 0)) (c1 (elt end 1)))
+	    ;; Store the relevant info but don't update yet
+	    (push (cons c0 (concat (cdr (assoc c0 chars))
+				   (concat "3" comstyle))) chars)
+	    (push (cons c1 (concat (cdr (assoc c1 chars)) "4")) chars)))))
+
+    ;; Process the chars that were part of a 2-char comment marker
+    (dolist (cs (nreverse chars))
+      (modify-syntax-entry (car cs)
+			   (concat (char-to-string (char-syntax (car cs)))
+				   " " (cdr cs))
+			   st))
+    (set-syntax-table st)))
+
+(defun generic-bracket-support ()
+  "Imenu support for [KEYWORD] constructs found in INF, INI and Samba files."
+  (setq imenu-generic-expression
+	'((nil "^\\[\\(.*\\)\\]" 1))
+        imenu-case-fold-search t))
+
+;;;###autoload
+(defun generic-make-keywords-list (keyword-list face &optional prefix suffix)
+  "Return a `font-lock-keywords' construct that highlights KEYWORD-LIST.
+KEYWORD-LIST is a list of keyword strings that should be
+highlighted with face FACE.  This function calculates a regular
+expression that matches these keywords and concatenates it with
+PREFIX and SUFFIX.  Then it returns a construct based on this
+regular expression that can be used as an element of
+`font-lock-keywords'."
+  (unless (listp keyword-list)
+    (error "Keywords argument must be a list of strings"))
+  (list (concat prefix "\\_<"
+		;; Use an optimized regexp.
+		(regexp-opt keyword-list t)
+		"\\_>" suffix)
+	1
+	face))
+
+(provide 'generic)
+
+;; arch-tag: 239c1fc4-1303-48d9-9ac0-657d655669ea
+;;; generic.el ends here