Function: forms-mode

forms-mode is an autoloaded, interactive and byte-compiled function defined in forms.el.gz.

Signature

(forms-mode &optional PRIMARY)

Documentation

Major mode to visit files in a field-structured manner using a form.

Commands: Equivalent keys in read-only mode:
 TAB forms-next-field TAB
 C-c TAB forms-next-field
 C-c < forms-first-record <
 C-c > forms-last-record >
 C-c ? describe-mode ?
 C-c C-k forms-delete-record
 C-c C-q forms-toggle-read-only q
 C-c C-o forms-insert-record
 C-c C-l forms-jump-record l
 C-c C-n forms-next-record n
 C-c C-p forms-prev-record p
 C-c C-r forms-search-reverse r
 C-c C-s forms-search-forward s
 C-c C-x forms-exit x

Key Bindings

Source Code

;; Defined in /usr/src/emacs/lisp/forms.el.gz
;;;###autoload
(defun forms-mode (&optional primary)
  ;; FIXME: use define-derived-mode
  "Major mode to visit files in a field-structured manner using a form.

Commands:                        Equivalent keys in read-only mode:
 TAB            forms-next-field          TAB
 C-c TAB        forms-next-field
 C-c <          forms-first-record         <
 C-c >          forms-last-record          >
 C-c ?          describe-mode              ?
 C-c C-k        forms-delete-record
 C-c C-q        forms-toggle-read-only     q
 C-c C-o        forms-insert-record
 C-c C-l        forms-jump-record          l
 C-c C-n        forms-next-record          n
 C-c C-p        forms-prev-record          p
 C-c C-r        forms-search-reverse       r
 C-c C-s        forms-search-forward       s
 C-c C-x        forms-exit                 x"
  (interactive)

  ;; This is not a simple major mode, as usual.  Therefore, forms-mode
  ;; takes an optional argument `primary' which is used for the
  ;; initial set-up.  Normal use would leave `primary' to nil.
  ;; A global buffer-local variable `forms--mode-setup' has the same
  ;; effect but makes it possible to auto-invoke forms-mode using
  ;; `find-file'.
  ;; Note: although it seems logical to have `make-local-variable'
  ;; executed where the variable is first needed, I have deliberately
  ;; placed all calls in this function.

  ;; Primary set-up: evaluate buffer and check if the mandatory
  ;; variables have been set.
  (if (or primary (not forms--mode-setup))
      (progn
	;;(message "forms: setting up...")
	(kill-all-local-variables)

	;; Make mandatory variables.
	(make-local-variable 'forms-file)
	(make-local-variable 'forms-number-of-fields)
	(make-local-variable 'forms-format-list)

	;; Make optional variables.
	(make-local-variable 'forms-field-sep)
        (make-local-variable 'forms-read-only)
        (make-local-variable 'forms-multi-line)
	(make-local-variable 'forms-forms-scroll)
	(make-local-variable 'forms-forms-jump)
	(make-local-variable 'forms-insert-after)
	(make-local-variable 'forms-use-text-properties)

        ;; Make sure no filters exist.
        (setq-local forms-read-file-filter nil)
        (setq-local forms-write-file-filter nil)
        (setq-local forms-new-record-filter nil)
        (setq-local forms-modified-record-filter nil)

	;; Setup faces to show read-only and read-write fields.
	(make-local-variable 'forms-ro-face)
	(make-local-variable 'forms-rw-face)

	;; eval the buffer, should set variables
	;;(message "forms: processing control file...")
	;; If enable-local-eval is not set to t the user is asked first.
	(if (or (eq enable-local-eval t)
		(yes-or-no-p
		 (concat "Evaluate lisp code in buffer "
			 (buffer-name) " to display forms? ")))
	    (eval-buffer)
	  (error "`enable-local-eval' inhibits buffer evaluation"))

	;; Check if the mandatory variables make sense.
	(or forms-file
	    (error (concat "Forms control file error: "
			   "`forms-file' has not been set")))

	;; Check forms-field-sep first, since it can be needed to
	;; construct a default format list.
	(or (stringp forms-field-sep)
	    (error (concat "Forms control file error: "
			   "`forms-field-sep' is not a string")))

	(if forms-number-of-fields
	    (or (and (numberp forms-number-of-fields)
		     (> forms-number-of-fields 0))
		(error (concat "Forms control file error: "
			       "`forms-number-of-fields' must be a number > 0")))
	  (or (null forms-format-list)
	      (error (concat "Forms control file error: "
			     "`forms-number-of-fields' has not been set"))))

	(or forms-format-list
	    (forms--intuit-from-file))

	(if forms-multi-line
	    (if (and (stringp forms-multi-line)
		     (eq (length forms-multi-line) 1))
		(if (string= forms-multi-line forms-field-sep)
		    (error (concat "Forms control file error: "
				   "`forms-multi-line' is equal to `forms-field-sep'")))
	      (error (concat "Forms control file error: "
			     "`forms-multi-line' must be nil or a one-character string"))))

	;; Validate and process forms-format-list.
	;;(message "forms: pre-processing format list...")
	(make-local-variable 'forms--elements)
	(forms--process-format-list)

	;; Build the formatter and parser.
	;;(message "forms: building formatter...")
	(make-local-variable 'forms--format)
	(make-local-variable 'forms--markers)
	(make-local-variable 'forms--dyntexts)
	;;(message "forms: building parser...")
	(forms--make-format)
	(make-local-variable 'forms--parser)
	(forms--make-parser)
	;;(message "forms: building parser... done.")

	;; Check if record filters are defined.
	(if (and forms-new-record-filter
		 (not (functionp forms-new-record-filter)))
	    (error (concat "Forms control file error: "
			   "`forms-new-record-filter' is not a function")))

	(if (and forms-modified-record-filter
		 (not (functionp forms-modified-record-filter)))
	    (error (concat "Forms control file error: "
			   "`forms-modified-record-filter' is not a function")))

	;; The filters access the contents of the forms using `forms-fields'.
	(make-local-variable 'forms-fields)

	;; Dynamic text support.
	(make-local-variable 'forms--dynamic-text)

	;; Prevent accidental overwrite of the control file and auto-save.
        ;; We bind change-major-mode-with-file-name to nil to prevent
        ;; set-visited-file-name from calling set-auto-mode, which
        ;; might kill all local variables and set forms-file nil,
        ;; which will then barf in find-file-noselect below.  This can
        ;; happen when the user sets the default major mode that is
        ;; different from the Fundamental mode.
        (let (change-major-mode-with-file-name)
          (set-visited-file-name nil))

	;; Prepare this buffer for further processing.
	(setq buffer-read-only nil)
	(erase-buffer)

	;;(message "forms: setting up... done.")
	))

  ;; initialization done
  (setq forms--mode-setup t)

  ;; Copy desired faces to the actual variables used by the forms formatter.
  (make-local-variable 'forms--ro-face)
  (make-local-variable 'forms--rw-face)
  (if forms-read-only
      (progn
	(setq forms--ro-face forms-ro-face)
	(setq forms--rw-face forms-ro-face))
    (setq forms--ro-face forms-ro-face)
    (setq forms--rw-face forms-rw-face))

  ;; Make more local variables.
  (make-local-variable 'forms--file-buffer)
  (make-local-variable 'forms--total-records)
  (make-local-variable 'forms--current-record)
  (make-local-variable 'forms--the-record-list)
  (make-local-variable 'forms--search-regexp)

  ;; The keymaps are global, so multiple forms mode buffers can share them.
  ;;(make-local-variable 'forms-mode-map)
  ;;(make-local-variable 'forms-mode-ro-map)
  ;;(make-local-variable 'forms-mode-edit-map)
  (if forms-mode-map			; already defined
      nil
    ;;(message "forms: building keymap...")
    (forms--mode-commands)
    ;;(message "forms: building keymap... done.")
    )

  ;; set the major mode indicator
  (setq major-mode 'forms-mode)
  (setq mode-name "Forms")

  (cursor-intangible-mode 1)

  ;; find the data file
  (setq forms--file-buffer (find-file-noselect forms-file))

  ;; Pre-transform.
  (let ((read-file-filter forms-read-file-filter)
	(write-file-filter forms-write-file-filter))
    (if read-file-filter
	(with-current-buffer forms--file-buffer
	  (let ((inhibit-read-only t)
		(file-modified (buffer-modified-p)))
	    (forms--run-functions read-file-filter)
	    (if (not file-modified) (set-buffer-modified-p nil)))
	  (if write-file-filter
	      (add-hook 'write-file-functions write-file-filter nil t)))
      (if write-file-filter
	  (with-current-buffer forms--file-buffer
	    (add-hook 'write-file-functions write-file-filter nil t)))))

  ;; count the number of records, and set see if it may be modified
  (let (ro)
    (setq forms--total-records
	  (with-current-buffer forms--file-buffer
	    (prog1
		(progn
		  ;;(message "forms: counting records...")
		  (bury-buffer (current-buffer))
		  (setq ro buffer-read-only)
		  (count-lines (point-min) (point-max)))
	      ;;(message "forms: counting records... done.")
	      )))
    (if ro
	(setq forms-read-only t)))

  ;;(message "forms: proceeding setup...")

  ;; Since we aren't really implementing a minor mode, we hack the mode line
  ;; directly to get the text " View " into forms-read-only form buffers.  For
  ;; that reason, this variable must be buffer only.
  (make-local-variable 'minor-mode-alist)
  (setq minor-mode-alist (list (list 'forms-read-only " View")))

  ;;(message "forms: proceeding setup (keymaps)...")
  (forms--set-keymaps)
  ;;(message "forms: proceeding setup (commands)...")
  (forms--change-commands)

  ;;(message "forms: proceeding setup (buffer)...")
  (set-buffer-modified-p nil)

  (if (= forms--total-records 0)
      ;;(message "forms: proceeding setup (new file)...")
      (progn
	(insert
	 "GNU Emacs Forms Mode\n\n"
	 (if (file-exists-p forms-file)
	     (format-message
	      "No records available in file `%s'\n\n" forms-file)
	   (format-message
	    "Creating new file `%s'\nwith %d field%s per record\n\n"
	    forms-file forms-number-of-fields
	    (if (= 1 forms-number-of-fields) "" "s")))
	 "Use " (substitute-command-keys "\\[forms-insert-record]")
	 " to create new records.\n")
	(setq forms--current-record 1)
	(setq buffer-read-only t)
	(set-buffer-modified-p nil))

    ;; setup the first (or current) record to show
    (if (< forms--current-record 1)
	(setq forms--current-record 1))
    (forms-jump-record forms--current-record)

    (if forms-insert-after
	(forms-last-record)
      (forms-first-record))
    )

  ;; user customizing
  ;;(message "forms: proceeding setup (user hooks)...")
  (run-mode-hooks 'forms-mode-hook 'forms-mode-hooks)
  ;;(message "forms: setting up... done.")

  ;; be helpful
  (forms--help))