# HG changeset patch # User Richard M. Stallman # Date 678391573 0 # Node ID 7fede845e304417a00ec30f2ac8de9d3009b21ed # Parent 785babb5bb6f3033f1c090bc2e52da4b1107ddff *** empty log message *** diff -r 785babb5bb6f -r 7fede845e304 lisp/forms.el --- a/lisp/forms.el Mon Jul 01 18:05:53 1991 +0000 +++ b/lisp/forms.el Mon Jul 01 18:06:13 1991 +0000 @@ -1,9 +1,13 @@ -;;; Forms Mode - A GNU Emacs Major Mode ; @(#)@ forms 1.2.2 -;;; Created 1989 - Johan Vromans -;;; See the docs for a list of other contributors. -;;; +;;; forms.el -- Forms Mode - A GNU Emacs Major Mode +;;; SCCS Status : @(#)@ forms 1.2.7 +;;; Author : Johan Vromans +;;; Created On : 1989 +;;; Last Modified By: Johan Vromans +;;; Last Modified On: Mon Jul 1 14:13:20 1991 +;;; Update Count : 15 +;;; Status : OK + ;;; This file is part of GNU Emacs. - ;;; GNU Emacs is distributed in the hope that it will be useful, ;;; but WITHOUT ANY WARRANTY. No author or distributor ;;; accepts responsibility to anyone for the consequences of using it @@ -20,6 +24,21 @@ ;;; Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. ;;; +;;; HISTORY +;;; 1-Jul-1991 Johan Vromans +;;; Normalized error messages. +;;; 30-Jun-1991 Johan Vromans +;;; Add support for forms-modified-record-filter. +;;; Allow the filter functions to be the name of a function. +;;; Fix: parse--format used forms--dynamic-text destructively. +;;; Internally optimized the forms-format-list. +;;; Added support for debugging. +;;; Stripped duplicate documentation. +;;; +;;; 29-Jun-1991 Johan Vromans +;;; Add support for functions and lisp symbols in forms-format-list. +;;; Add function forms-enumerate. + (provide 'forms-mode) ;;; Visit a file using a form. @@ -75,12 +94,20 @@ ;;; ;;; The forms-format-list should be a list, each element containing ;;; -;;; - either a string, e.g. "hello" (which is inserted \"as is\"), +;;; - a string, e.g. "hello" (which is inserted \"as is\"), ;;; ;;; - an integer, denoting a field number. The contents of the field ;;; are inserted at this point. ;;; The first field has number one. ;;; +;;; - a function call, e.g. (insert "text"). This function call is +;;; dynamically evaluated and should return a string. It should *NOT* +;;; have side-effects on the forms being constructed. +;;; The current fields are available to the function in the variable +;;; forms-fields, they should *NOT* be modified. +;;; +;;; - a lisp symbol, that must evaluate to one of the above. +;;; ;;; Optional variables which may be set in the control file: ;;; ;;; forms-field-sep [string, default TAB] @@ -111,10 +138,22 @@ ;;; to performs forms-first/last-field if in ;;; forms mode. ;;; -;;; forms-new-record-filter [function, no default] -;;; If defined: this function is called when a new +;;; forms-new-record-filter [symbol, no default] +;;; If defined: this should be the name of a +;;; function that is called when a new ;;; record is created. It can be used to fill in ;;; the new record with default fields, for example. +;;; Instead of the name of the function, it may +;;; be the function itself. +;;; +;;; forms-modified-record-filter [symbol, no default] +;;; If defined: this should be the name of a +;;; function that is called when a record has +;;; been modified. It is called after the fields +;;; are parsed. It can be used to register +;;; modification dates, for example. +;;; Instead of the name of the function, it may +;;; be the function itself. ;;; ;;; After evaluating the control file, its buffer is cleared and used ;;; for further processing. @@ -126,7 +165,7 @@ ;;; A record from the data file is transferred from the data file, ;;; split into fields (into forms--the-record-list), and displayed using ;;; the specs in forms-format-list. -;;; A format routine 'forms--format' is build upon startup to format +;;; A format routine 'forms--format' is built upon startup to format ;;; the records. ;;; ;;; When a form is changed the record is updated as soon as this form @@ -135,7 +174,7 @@ ;;; fields not shown on the forms retain their origional values. ;;; The newly formed record and replaces the contents of the ;;; old record in forms--file-buffer. -;;; A parse routine 'forms--parser' is build upon startup to parse +;;; A parse routine 'forms--parser' is built upon startup to parse ;;; the records. ;;; ;;; Two exit functions exist: forms-exit (which saves) and forms-exit-no-save @@ -196,7 +235,7 @@ ;;; ;;; Global variables and constants -(defconst forms-version "1.2.2" +(defconst forms-version "1.2.7" "Version of forms-mode implementation") (defvar forms-forms-scrolls t @@ -211,19 +250,10 @@ ;;; Mandatory variables - must be set by evaluating the control file (defvar forms-file nil - "Name of the file holding the data.") + "Name of the file holding the data.") (defvar forms-format-list nil - "Formatting specifications: - -It should be a list, each element containing - - - either a string, e.g. "hello" (which is inserted \"as is\"), - - - an integer, denoting the number of a field which contents are - inserted at this point. - The first field has number one. -") + "List of formatting specifications.") (defvar forms-number-of-fields nil "Number of fields per record.") @@ -288,6 +318,15 @@ (defvar forms--new-record-filter nil "Internal - set if a new record filter has been defined.") +(defvar forms--modified-record-filter nil + "Internal - set if a modified record filter has been defined.") + +(defvar forms--dynamic-text nil + "Internal - holds dynamic text to insert between fields.") + +(defvar forms-fields nil + "List with fields of the current forms. First field has number 1.") + ;;; ;;; forms-mode ;;; @@ -359,13 +398,29 @@ (make-local-variable 'forms--parser) (forms--make-parser) - ;; check if a new record filter was defined + ;; check if record filters are defined (make-local-variable 'forms--new-record-filter) (setq forms--new-record-filter - (and (fboundp 'forms-new-record-filter) - (symbol-function 'forms-new-record-filter))) + (cond + ((fboundp 'forms-new-record-filter) + (symbol-function 'forms-new-record-filter)) + ((and (boundp 'forms-new-record-filter) + (fboundp forms-new-record-filter)) + forms-new-record-filter))) (fmakunbound 'forms-new-record-filter) + (make-local-variable 'forms--modified-record-filter) + (setq forms--modified-record-filter + (cond + ((fboundp 'forms-modified-record-filter) + (symbol-function 'forms-modified-record-filter)) + ((and (boundp 'forms-modified-record-filter) + (fboundp forms-modified-record-filter)) + forms-modified-record-filter))) + (fmakunbound 'forms-modified-record-filter) + ;; dynamic text support + (make-local-variable 'forms--dynamic-text) + (make-local-variable 'forms-fields) ;; prepare this buffer for further processing (setq buffer-read-only nil) @@ -445,6 +500,9 @@ (defun forms--process-format-list () "Validate forms-format-list and set some global variables." + (forms--debug "forms-forms-list before 1st pass:\n" + 'forms-format-list) + ;; it must be non-nil (or forms-format-list (error "'forms-format-list' has not been set")) @@ -455,65 +513,65 @@ (setq forms--number-of-markers 0) (let ((the-list forms-format-list) ; the list of format elements + (this-item 0) ; element in list (field-num 0)) ; highest field number + (setq forms-format-list nil) ; gonna rebuild + (while the-list (let ((el (car-safe the-list)) (rem (cdr-safe the-list))) + ;; if it is a symbol, eval it first + (if (and (symbolp el) + (boundp el)) + (setq el (eval el))) + (cond ;; try string ... ((stringp el)) ; string is OK - ;; try int ... - ((numberp el) ; check it + ;; try numeric ... + ((numberp el) (if (or (<= el 0) (> el forms-number-of-fields)) (error - "forms error: field number %d out of range 1..%d" + "Forms error: field number %d out of range 1..%d" el forms-number-of-fields)) (setq forms--number-of-markers (1+ forms--number-of-markers)) (if (> el field-num) (setq field-num el))) + ;; try function + ((listp el) + (or (fboundp (car-safe el)) + (error + "Forms error: not a function: %s" + (prin1-to-string (car-safe el))))) + ;; else (t - (error "invalid element in 'forms-format-list': %s" - (prin1-to-string el))) - - ;; dead code - we'll need it in the future - ((consp el) ; check it - - (let ((str (car-safe el)) - (idx (cdr-safe el))) - - (cond - - ;; car must be string - ((not (stringp str)) - (error "forms error: car of cons %s must be string" - (prin1-to-string el))) - - ;; cdr must be number, > zero - ((or (not (numberp idx)) - (<= idx 0) - (> idx forms-number-of-fields)) - (error - "forms error: cdr of cons %s must be a number between 1 and %d" - (prin1-to-string el) - forms-number-of-fields))) - - ;; passed the test - handle it - (setq forms--number-of-markers (1+ forms--number-of-markers)) - (if (> idx field-num) - (setq field-num idx))))) + (error "Invalid element in 'forms-format-list': %s" + (prin1-to-string el)))) ;; advance to next element of the list - (setq the-list rem)))) + (setq the-list rem) + (setq forms-format-list + (append forms-format-list (list el) nil))))) + + (forms--debug "forms-forms-list after 1st pass:\n" + 'forms-format-list) + + ;; concat adjacent strings + (setq forms-format-list (forms--concat-adjacent forms-format-list)) + + (forms--debug "forms-forms-list after 2nd pass:\n" + 'forms-format-list + 'forms--number-of-markers) (setq forms--markers (make-vector forms--number-of-markers nil))) @@ -524,7 +582,7 @@ ;;; The format routine (forms--format) will look like ;;; ;;; (lambda (arg) -;;; +;;; (setq forms--dynamic-text nil) ;;; ;; "text: " ;;; (insert "text: ") ;;; ;; 6 @@ -532,6 +590,11 @@ ;;; (insert (elt arg 5)) ;;; ;; "\nmore text: " ;;; (insert "\nmore text: ") +;;; ;; (tocol 40) +;;; (let ((the-dyntext (tocol 40))) +;;; (insert the-dyntext) +;;; (setq forms--dynamic-text (append forms--dynamic-text +;;; (list the-dyntext)))) ;;; ;; 9 ;;; (aset forms--markers 1 (point-marker)) ;;; (insert (elt arg 8)) @@ -540,16 +603,17 @@ ;;; (defun forms--make-format () - "Generate parser function for forms" - (setq forms--format (forms--format-maker forms-format-list))) + "Generate format function for forms" + (setq forms--format (forms--format-maker forms-format-list)) + (forms--debug 'forms--format)) (defun forms--format-maker (the-format-list) "Returns the parser function for forms" (let ((the-marker 0)) (` (lambda (arg) + (setq forms--dynamic-text nil) (,@ (apply 'append - (mapcar 'forms--make-format-elt - (forms--concat-adjacent the-format-list)))))))) + (mapcar 'forms--make-format-elt the-format-list))))))) (defun forms--make-format-elt (el) (cond ((stringp el) @@ -558,7 +622,15 @@ (prog1 (` ((aset forms--markers (, the-marker) (point-marker)) (insert (elt arg (, (1- el)))))) - (setq the-marker (1+ the-marker)))))) + (setq the-marker (1+ the-marker)))) + ((listp el) + (prog1 + (` ((let ((the-dyntext (, el))) + (insert the-dyntext) + (setq forms--dynamic-text (append forms--dynamic-text + (list the-dyntext))))) + ))) + )) (defun forms--concat-adjacent (the-list) @@ -584,16 +656,22 @@ ;;; ;;; ;; "text: " ;;; (if (not (looking-at "text: ")) -;;; (error "parse error: cannot find \"text: \"")) +;;; (error "Parse error: cannot find \"text: \"")) ;;; (forward-char 6) ; past "text: " ;;; ;;; ;; 6 ;;; ;; "\nmore text: " ;;; (setq here (point)) ;;; (if (not (search-forward "\nmore text: " nil t nil)) -;;; (error "parse error: cannot find \"\\nmore text: \"")) +;;; (error "Parse error: cannot find \"\\nmore text: \"")) ;;; (aset the-recordv 5 (buffer-substring here (- (point) 12))) -;;; ... +;;; +;;; ;; (tocol 40) +;;; (let ((the-dyntext (car-safe forms--dynamic-text))) +;;; (if (not (looking-at (regexp-quote the-dyntext))) +;;; (error "Parse error: not looking at \"%s\"" the-dyntext)) +;;; (forward-char (length the-dyntext)) +;;; (setq forms--dynamic-text (cdr-safe forms--dynamic-text))) ;;; ... ;;; ;; final flush (due to terminator sentinel, see below) ;;; (aset the-recordv 7 (buffer-substring (point) (point-max))) @@ -601,16 +679,16 @@ (defun forms--make-parser () "Generate parser function for forms" - (setq forms--parser (forms--parser-maker forms-format-list))) + (setq forms--parser (forms--parser-maker forms-format-list)) + (forms--debug 'forms--parser)) (defun forms--parser-maker (the-format-list) "Returns the parser function for forms" (let ((the-field nil) (seen-text nil) the--format-list) - ;; concat adjacent strings and add a terminator sentinel - (setq the--format-list - (append (forms--concat-adjacent the-format-list) (list nil))) + ;; add a terminator sentinel + (setq the--format-list (append the-format-list (list nil))) (` (lambda nil (let (here) (goto-char (point-min)) @@ -618,30 +696,50 @@ (mapcar 'forms--make-parser-elt the--format-list)))))))) (defun forms--make-parser-elt (el) - (cond ((stringp el) - (prog1 - (if the-field - (` ((setq here (point)) - (if (not (search-forward (, el) nil t nil)) - (error "Parse error: cannot find %s" (, el))) - (aset the-recordv (, (1- the-field)) - (buffer-substring here - (- (point) (, (length el))))))) - (` ((if (not (looking-at (, (regexp-quote el)))) - (error "Parse error: not looking at %s" (, el))) - (forward-char (, (length el)))))) - (setq seen-text t) - (setq the-field nil))) - ((numberp el) - (if the-field - (error "Cannot parse adjacent fields %d and %d" - the-field el) - (setq the-field el) - nil)) - ((null el) - (if the-field - (` ((aset the-recordv (, (1- the-field)) - (buffer-substring (point) (point-max))))))))) + (cond + ((stringp el) + (prog1 + (if the-field + (` ((setq here (point)) + (if (not (search-forward (, el) nil t nil)) + (error "Parse error: cannot find \"%s\"" (, el))) + (aset the-recordv (, (1- the-field)) + (buffer-substring here + (- (point) (, (length el))))))) + (` ((if (not (looking-at (, (regexp-quote el)))) + (error "Parse error: not looking at \"%s\"" (, el))) + (forward-char (, (length el)))))) + (setq seen-text t) + (setq the-field nil))) + ((numberp el) + (if the-field + (error "Cannot parse adjacent fields %d and %d" + the-field el) + (setq the-field el) + nil)) + ((null el) + (if the-field + (` ((aset the-recordv (, (1- the-field)) + (buffer-substring (point) (point-max))))))) + ((listp el) + (prog1 + (if the-field + (` ((let ((here (point)) + (the-dyntext (car-safe forms--dynamic-text))) + (if (not (search-forward the-dyntext nil t nil)) + (error "Parse error: cannot find \"%s\"" the-dyntext)) + (aset the-recordv (, (1- the-field)) + (buffer-substring here + (- (point) (length the-dyntext)))) + (setq forms--dynamic-text (cdr-safe forms--dynamic-text))))) + (` ((let ((the-dyntext (car-safe forms--dynamic-text))) + (if (not (looking-at (regexp-quote the-dyntext))) + (error "Parse error: not looking at \"%s\"" the-dyntext)) + (forward-char (length the-dyntext)) + (setq forms--dynamic-text (cdr-safe forms--dynamic-text)))))) + (setq seen-text t) + (setq the-field nil))) + )) ;;; (defun forms--set-minor-mode () @@ -699,7 +797,7 @@ nil (fset 'forms--scroll-down (symbol-function 'scroll-down)) (fset 'scroll-down - '(lambda (arg) + '(lambda (&optional arg) (interactive "P") (if (and forms--mode-setup forms-forms-scroll) @@ -712,7 +810,7 @@ nil (fset 'forms--scroll-up (symbol-function 'scroll-up)) (fset 'scroll-up - '(lambda (arg) + '(lambda (&optional arg) (interactive "P") (if (and forms--mode-setup forms-forms-scroll) @@ -860,6 +958,7 @@ ""))))) ;; call the formatter function + (setq forms-fields (append (list nil) forms--the-record-list nil)) (funcall forms--format forms--the-record-list) ;; prepare @@ -884,10 +983,18 @@ (setq the-recordv (vconcat forms--the-record-list)) ;; parse the form and update the vector - (funcall forms--parser) + (let ((forms--dynamic-text forms--dynamic-text)) + (funcall forms--parser)) - ;; transform to a list and return - (append the-recordv nil))) + (if forms--modified-record-filter + ;; As a service to the user, we add a zeroth element so she + ;; can use the same indices as in the forms definition. + (let ((the-fields (vconcat [nil] the-recordv))) + (setq the-fields (funcall forms--modified-record-filter the-fields)) + (cdr (append the-fields nil))) + + ;; transform to a list and return + (append the-recordv nil)))) (defun forms--update () "Update current record with contents of form. As a side effect: sets @@ -1065,16 +1172,18 @@ (forms-mode)))) ;; Sample: -;; (defun forms-new-record-filter (the-fields) +;; (defun my-new-record-filter (the-fields) ;; ;; numbers are relative to 1 ;; (aset the-fields 4 (current-time-string)) ;; (aset the-fields 6 (user-login-name)) ;; the-list) +;; (setq forms-new-record-filter 'my-new-record-filter) (defun forms-insert-record (arg) "Create a new record before the current one. With ARG: store the record after the current one. - If a function forms-new-record-filter is defined, is is called to + If a function forms-new-record-filter is defined, or forms-new-record-filter + contains the name of a function, it is called to fill (some of) the fields with default values." ; The above doc is not true, but for documentary purposes only @@ -1193,3 +1302,55 @@ (setq i (1+ i)))) nil (goto-char (aref forms--markers 0))))) + +;;; +;;; Special service +;;; +(defun forms-enumerate (the-fields) + "Take a quoted list of symbols, and set their values to the numbers +1, 2 and so on. Returns the higest number. + +Usage: (setq forms-number-of-fields + (forms-enumerate + '(field1 field2 field2 ...)))" + + (let ((the-index 0)) + (while the-fields + (setq the-index (1+ the-index)) + (let ((el (car-safe the-fields))) + (setq the-fields (cdr-safe the-fields)) + (set el the-index))) + the-index)) + +;;; +;;; Debugging +;;; +(defvar forms--debug nil + "*Enables forms-mode debugging if not nil.") + +(defun forms--debug (&rest args) + "Internal - debugging routine" + (if forms--debug + (let ((ret nil)) + (while args + (let ((el (car-safe args))) + (setq args (cdr-safe args)) + (if (stringp el) + (setq ret (concat ret el)) + (setq ret (concat ret (prin1-to-string el) " = ")) + (if (boundp el) + (let ((vel (eval el))) + (setq ret (concat ret (prin1-to-string vel) "\n"))) + (setq ret (concat ret "" "\n"))) + (if (fboundp el) + (setq ret (concat ret (prin1-to-string (symbol-function el)) + "\n")))))) + (save-excursion + (set-buffer (get-buffer-create "*forms-mode debug*")) + (goto-char (point-max)) + (insert ret))))) + +;;; Local Variables: +;;; eval: (headers) +;;; eval: (setq comment-start ";;; ") +;;; End: