comparison lisp/forms.el @ 4121:25d32add267c

Rewritten by Vromans to use text properties.
author Richard M. Stallman <rms@gnu.org>
date Sat, 17 Jul 1993 19:15:19 +0000
parents d8c9bc546c87
children 6a3f5f51897b
comparison
equal deleted inserted replaced
4120:872d8ef4bb62 4121:25d32add267c
1 ;;; forms.el -- Forms mode: edit a file as a form to fill in. 1 ;;; forms.el -- Forms mode: edit a file as a form to fill in.
2 ;;; Copyright (C) 1991, 1993 Free Software Foundation, Inc. 2 ;;; Copyright (C) 1991, 1993 Free Software Foundation, Inc.
3 3
4 ;; Author: Johan Vromans <jv@mh.nl> 4 ;; Author: Johan Vromans <jv@mh.nl>
5 ;; Version: 1.2.14 5 ;; Version: 2.0
6 ;; Keywords: non-text
7 6
8 ;; This file is part of GNU Emacs. 7 ;; This file is part of GNU Emacs.
9 8
10 ;; GNU Emacs is free software; you can redistribute it and/or modify 9 ;; GNU Emacs is free software; you can redistribute it and/or modify
11 ;; it under the terms of the GNU General Public License as published by 10 ;; it under the terms of the GNU General Public License as published by
31 ;;; Names which start with 'form--' are intended for internal use, and 30 ;;; Names which start with 'form--' are intended for internal use, and
32 ;;; should *NOT* be used from the outside. 31 ;;; should *NOT* be used from the outside.
33 ;;; 32 ;;;
34 ;;; All variables are buffer-local, to enable multiple forms visits 33 ;;; All variables are buffer-local, to enable multiple forms visits
35 ;;; simultaneously. 34 ;;; simultaneously.
36 ;;; Variable 'forms--mode-setup' is local to *ALL* buffers, for it 35 ;;; Variable `forms--mode-setup' is local to *ALL* buffers, for it
37 ;;; controls if forms-mode has been enabled in a buffer. 36 ;;; controls if forms-mode has been enabled in a buffer.
38 ;;; 37 ;;;
39 ;;; === How it works === 38 ;;; === How it works ===
40 ;;; 39 ;;;
41 ;;; Forms mode means visiting a data file which is supposed to consist 40 ;;; Forms mode means visiting a data file which is supposed to consist
42 ;;; of records each containing a number of fields. The records are 41 ;;; of records each containing a number of fields. The records are
43 ;;; separated by a newline, the fields are separated by a user-defined 42 ;;; separated by a newline, the fields are separated by a user-defined
44 ;;; field separater (default: TAB). 43 ;;; field separater (default: TAB).
45 ;;; When shown, a record is transferred to an emacs buffer and 44 ;;; When shown, a record is transferred to an Emacs buffer and
46 ;;; presented using a user-defined form. One record is shown at a 45 ;;; presented using a user-defined form. One record is shown at a
47 ;;; time. 46 ;;; time.
48 ;;; 47 ;;;
49 ;;; Forms mode is a composite mode. It involves two files, and two 48 ;;; Forms mode is a composite mode. It involves two files, and two
50 ;;; buffers. 49 ;;; buffers.
52 ;;; data file and the forms format. This file buffer will be used to 51 ;;; data file and the forms format. This file buffer will be used to
53 ;;; present the forms. 52 ;;; present the forms.
54 ;;; The second file holds the actual data. The buffer of this file 53 ;;; The second file holds the actual data. The buffer of this file
55 ;;; will be buried, for it is never accessed directly. 54 ;;; will be buried, for it is never accessed directly.
56 ;;; 55 ;;;
57 ;;; Forms mode is invoked using "forms-find-file control-file". 56 ;;; Forms mode is invoked using M-x forms-find-file control-file .
58 ;;; Alternativily forms-find-file-other-window can be used. 57 ;;; Alternativily `forms-find-file-other-window' can be used.
59 ;;; 58 ;;;
60 ;;; You may also visit the control file, and switch to forms mode by hand 59 ;;; You may also visit the control file, and switch to forms mode by hand
61 ;;; with M-x forms-mode . 60 ;;; with M-x forms-mode .
62 ;;; 61 ;;;
63 ;;; Automatic mode switching is supported, so you may use "find-file" 62 ;;; Automatic mode switching is supported if you specify
64 ;;; if you specify "-*- forms -*-" in the first line of the control file. 63 ;;; "-*- forms -*-" in the first line of the control file.
65 ;;; 64 ;;;
66 ;;; The control file is visited, evaluated using 65 ;;; The control file is visited, evaluated using `eval-current-buffer',
67 ;;; eval-current-buffer, and should set at least the following 66 ;;; and should set at least the following variables:
68 ;;; variables: 67 ;;;
69 ;;; 68 ;;; forms-file [string]
70 ;;; forms-file [string] the name of the data file. 69 ;;; The name of the data file.
71 ;;; 70 ;;;
72 ;;; forms-number-of-fields [integer] 71 ;;; forms-number-of-fields [integer]
73 ;;; The number of fields in each record. 72 ;;; The number of fields in each record.
74 ;;; 73 ;;;
75 ;;; forms-format-list [list] formatting instructions. 74 ;;; forms-format-list [list]
76 ;;; 75 ;;; Formatting instructions.
77 ;;; The forms-format-list should be a list, each element containing 76 ;;;
78 ;;; 77 ;;; `forms-format-list' should be a list, each element containing
79 ;;; - a string, e.g. "hello" (which is inserted \"as is\"), 78 ;;;
80 ;;; 79 ;;; - a string, e.g. "hello". The string is inserted in the forms
81 ;;; - an integer, denoting a field number. The contents of the field 80 ;;; "as is".
82 ;;; are inserted at this point. 81 ;;;
83 ;;; The first field has number one. 82 ;;; - an integer, denoting a field number.
84 ;;; 83 ;;; The contents of this field are inserted at this point.
85 ;;; - a function call, e.g. (insert "text"). This function call is 84 ;;; Fields are numbered starting with number one.
86 ;;; dynamically evaluated and should return a string. It should *NOT* 85 ;;;
87 ;;; have side-effects on the forms being constructed. 86 ;;; - a function call, e.g. (insert "text").
88 ;;; The current fields are available to the function in the variable 87 ;;; This function call is dynamically evaluated and should return a
89 ;;; forms-fields, they should *NOT* be modified. 88 ;;; string. It should *NOT* have side-effects on the forms being
90 ;;; 89 ;;; constructed. The current fields are available to the function
91 ;;; - a lisp symbol, that must evaluate to one of the above. 90 ;;; in the variable `forms-fields', they should *NOT* be modified.
91 ;;;
92 ;;; - a lisp symbol, that must evaluate to one of the above.
92 ;;; 93 ;;;
93 ;;; Optional variables which may be set in the control file: 94 ;;; Optional variables which may be set in the control file:
94 ;;; 95 ;;;
95 ;;; forms-field-sep [string, default TAB] 96 ;;; forms-field-sep [string, default TAB]
96 ;;; The field separator used to separate the 97 ;;; The field separator used to separate the
97 ;;; fields in the data file. It may be a string. 98 ;;; fields in the data file. It may be a string.
98 ;;; 99 ;;;
99 ;;; forms-read-only [bool, default nil] 100 ;;; forms-read-only [bool, default nil]
100 ;;; 't' means that the data file is visited read-only. 101 ;;; Non-nil means that the data file is visited
102 ;;; read-only (view mode) as opposed to edit mode.
101 ;;; If no write access to the data file is 103 ;;; If no write access to the data file is
102 ;;; possible, read-only mode is enforced. 104 ;;; possible, view mode is enforced.
103 ;;; 105 ;;;
104 ;;; forms-multi-line [string, default "^K"] 106 ;;; forms-multi-line [string, default "^K"]
105 ;;; If non-null the records of the data file may 107 ;;; If non-null the records of the data file may
106 ;;; contain fields which span multiple lines in 108 ;;; contain fields that can span multiple lines in
107 ;;; the form. 109 ;;; the form.
108 ;;; This variable denoted the separator character 110 ;;; This variable denotes the separator character
109 ;;; to be used for this purpose. Upon display, all 111 ;;; to be used for this purpose. Upon display, all
110 ;;; occurrencies of this character are translated 112 ;;; occurrencies of this character are translated
111 ;;; to newlines. Upon storage they are translated 113 ;;; to newlines. Upon storage they are translated
112 ;;; back to the separator. 114 ;;; back to the separator character.
113 ;;; 115 ;;;
114 ;;; forms-forms-scroll [bool, default t] 116 ;;; forms-forms-scroll [bool, default t]
115 ;;; If non-nil: redefine scroll-up/down to perform 117 ;;; Non-nil means: rebind locally the commands that
116 ;;; forms-next/prev-field if in forms mode. 118 ;;; perform `scroll-up' or `scroll-down' to use
119 ;;; `forms-next-field' resp. `forms-prev-field'.
117 ;;; 120 ;;;
118 ;;; forms-forms-jump [bool, default t] 121 ;;; forms-forms-jump [bool, default t]
119 ;;; If non-nil: redefine beginning/end-of-buffer 122 ;;; Non-nil means: rebind locally the commands that
120 ;;; to performs forms-first/last-field if in 123 ;;; perform `beginning-of-buffer' or `end-of-buffer'
121 ;;; forms mode. 124 ;;; to perform `forms-first-field' resp. `forms-last-field'.
122 ;;; 125 ;;;
123 ;;; forms-new-record-filter [symbol, no default] 126 ;;; forms-new-record-filter [symbol, no default]
124 ;;; If defined: this should be the name of a 127 ;;; If defined: this should be the name of a
125 ;;; function that is called when a new 128 ;;; function that is called when a new
126 ;;; record is created. It can be used to fill in 129 ;;; record is created. It can be used to fill in
135 ;;; are parsed. It can be used to register 138 ;;; are parsed. It can be used to register
136 ;;; modification dates, for example. 139 ;;; modification dates, for example.
137 ;;; Instead of the name of the function, it may 140 ;;; Instead of the name of the function, it may
138 ;;; be the function itself. 141 ;;; be the function itself.
139 ;;; 142 ;;;
143 ;;; forms-use-text-properties [bool, see text for default]
144 ;;; This variable controls if forms mode should use
145 ;;; text properties to protect the form text from being
146 ;;; modified (using text-property `read-only').
147 ;;; Also, the read-write fields are shown using a
148 ;;; distinct face, if possible.
149 ;;; This variable defaults to t if running Emacs 19
150 ;;; with text properties.
151 ;;; The default face to show read-write fields is
152 ;;; copied from face `region'.
153 ;;;
154 ;;; forms-ro-face [symbol, default 'default]
155 ;;; This is the face that is used to show
156 ;;; read-only text on the screen.If used, this
157 ;;; variable should be set to a symbol that is a
158 ;;; valid face.
159 ;;; E.g.
160 ;;; (make-face 'my-face)
161 ;;; (setq forms-ro-face 'my-face)
162 ;;;
163 ;;; forms-rw-face [symbol, default 'region]
164 ;;; This is the face that is used to show
165 ;;; read-write text on the screen.
166 ;;;
140 ;;; After evaluating the control file, its buffer is cleared and used 167 ;;; After evaluating the control file, its buffer is cleared and used
141 ;;; for further processing. 168 ;;; for further processing.
142 ;;; The data file (as designated by "forms-file") is visited in a buffer 169 ;;; The data file (as designated by `forms-file') is visited in a buffer
143 ;;; (forms--file-buffer) which will not normally be shown. 170 ;;; `forms--file-buffer' which will not normally be shown.
144 ;;; Great malfunctioning may be expected if this file/buffer is modified 171 ;;; Great malfunctioning may be expected if this file/buffer is modified
145 ;;; outside of this package while it's being visited! 172 ;;; outside of this package while it is being visited!
146 ;;; 173 ;;;
147 ;;; A record from the data file is transferred from the data file, 174 ;;; Normal operation is to transfer one line (record) from the data file,
148 ;;; split into fields (into forms--the-record-list), and displayed using 175 ;;; split it into fields (into `forms--the-record-list'), and display it
149 ;;; the specs in forms-format-list. 176 ;;; using the specs in `forms-format-list'.
150 ;;; A format routine 'forms--format' is built upon startup to format 177 ;;; A format routine `forms--format' is built upon startup to format
178 ;;; the records according to `forms-format-list'.
179 ;;;
180 ;;; When a form is changed the record is updated as soon as this form
181 ;;; is left. The contents of the form are parsed using information
182 ;;; obtained from `forms-format-list', and the fields which are
183 ;;; deduced from the form are modified. Fields not shown on the forms
184 ;;; retain their origional values. The newly formed record then
185 ;;; replaces the contents of the old record in `forms--file-buffer'.
186 ;;; A parse routine `forms--parser' is built upon startup to parse
151 ;;; the records. 187 ;;; the records.
152 ;;; 188 ;;;
153 ;;; When a form is changed the record is updated as soon as this form 189 ;;; Two exit functions exist: `forms-exit' and `forms-exit-no-save'.
154 ;;; is left. The contents of the form are parsed using forms-format-list, 190 ;;; `forms-exit' saves the data to the file, if modified.
155 ;;; and the fields which are deduced from the form are modified. So, 191 ;;; `forms-exit-no-save` does not. However, if `forms-exit-no-save'
156 ;;; fields not shown on the forms retain their origional values. 192 ;;; is executed and the file buffer has been modified, Emacs will ask
157 ;;; The newly formed record and replaces the contents of the 193 ;;; questions anyway.
158 ;;; old record in forms--file-buffer. 194 ;;;
159 ;;; A parse routine 'forms--parser' is built upon startup to parse 195 ;;; Other functions provided by forms mode are:
160 ;;; the records.
161 ;;;
162 ;;; Two exit functions exist: forms-exit (which saves) and forms-exit-no-save
163 ;;; (which doesn't). However, if forms-exit-no-save is executed and the file
164 ;;; buffer has been modified, emacs will ask questions.
165 ;;;
166 ;;; Other functions are:
167 ;;; 196 ;;;
168 ;;; paging (forward, backward) by record 197 ;;; paging (forward, backward) by record
169 ;;; jumping (first, last, random number) 198 ;;; jumping (first, last, random number)
170 ;;; searching 199 ;;; searching
171 ;;; creating and deleting records 200 ;;; creating and deleting records
177 ;;; file (using forms-last-record) will adjust forms--total-records if 206 ;;; file (using forms-last-record) will adjust forms--total-records if
178 ;;; needed. 207 ;;; needed.
179 ;;; 208 ;;;
180 ;;; Commands and keymaps: 209 ;;; Commands and keymaps:
181 ;;; 210 ;;;
182 ;;; A local keymap 'forms-mode-map' is used in the forms buffer. 211 ;;; A local keymap `forms-mode-map' is used in the forms buffer.
183 ;;; As conventional, this map can be accessed with C-c prefix. 212 ;;; If the forms is in view mode, this keymap is used so all forms mode
184 ;;; In read-only mode, the C-c prefix must be omitted. 213 ;;; functions are accessible.
214 ;;; If the forms is in edit mode, this map can be accessed with C-c prefix.
185 ;;; 215 ;;;
186 ;;; Default bindings: 216 ;;; Default bindings:
187 ;;; 217 ;;;
188 ;;; \C-c forms-mode-map 218 ;;; \C-c forms-mode-map
189 ;;; TAB forms-next-field 219 ;;; TAB forms-next-field
201 ;;; s forms-search 231 ;;; s forms-search
202 ;;; v forms-view-mode 232 ;;; v forms-view-mode
203 ;;; x forms-exit-no-save 233 ;;; x forms-exit-no-save
204 ;;; DEL forms-prev-record 234 ;;; DEL forms-prev-record
205 ;;; 235 ;;;
206 ;;; The bindings of standard functions scroll-up, scroll-down, 236 ;;; For convenience, TAB is always bound to `forms-next-field', so you
207 ;;; beginning-of-buffer and end-of-buffer are locally replaced with 237 ;;; don't need the C-c prefix for this command.
238 ;;;
239 ;;; As mentioned above (see `forms-forms-scroll' and `forms-forms-jump')
240 ;;; the bindings of standard functions `scroll-up', `scroll-down',
241 ;;; `beginning-of-buffer' and `end-of-buffer' are locally replaced with
208 ;;; forms mode functions next/prev record and first/last 242 ;;; forms mode functions next/prev record and first/last
209 ;;; record. Buffer-local variables forms-forms-scroll and 243 ;;; record.
210 ;;; forms-forms-jump (default: t) may be set to nil to inhibit 244 ;;;
211 ;;; rebinding. 245 ;;; `local-write-file hook' is defined to save the actual data file
212 ;;; 246 ;;; instead of the buffer data, `revert-file-hook' is defined to
213 ;;; A local-write-file hook is defined to save the actual data file
214 ;;; instead of the buffer data, a revert-file-hook is defined to
215 ;;; revert a forms to original. 247 ;;; revert a forms to original.
216 ;;;
217 ;;; For convenience, TAB is always bound to forms-next-field, so you
218 ;;; don't need the C-c prefix for this command.
219 248
220 ;;; Code: 249 ;;; Code:
221 250
222 ;;; Global variables and constants 251 ;;; Global variables and constants:
223 252
224 (provide 'forms) ;;; official 253 (provide 'forms) ;;; official
225 (provide 'forms-mode) ;;; for compatibility 254 (provide 'forms-mode) ;;; for compatibility
226 255
227 (defconst forms-version "1.2.14" 256 (defconst forms-version "2.0"
228 "Version of forms-mode implementation.") 257 "Version of forms-mode implementation.")
229 258
230 (defvar forms-mode-hooks nil 259 (defvar forms-mode-hooks nil
231 "Hook functions to be run upon entering Forms mode.") 260 "Hook functions to be run upon entering Forms mode.")
232 261
233 ;;; Mandatory variables - must be set by evaluating the control file 262 ;;; Mandatory variables - must be set by evaluating the control file.
234 263
235 (defvar forms-file nil 264 (defvar forms-file nil
236 "Name of the file holding the data.") 265 "Name of the file holding the data.")
237 266
238 (defvar forms-format-list nil 267 (defvar forms-format-list nil
239 "List of formatting specifications.") 268 "List of formatting specifications.")
240 269
241 (defvar forms-number-of-fields nil 270 (defvar forms-number-of-fields nil
242 "Number of fields per record.") 271 "Number of fields per record.")
243 272
244 ;;; Optional variables with default values 273 ;;; Optional variables with default values.
245 274
246 (defvar forms-field-sep "\t" 275 (defvar forms-field-sep "\t"
247 "Field separator character (default TAB).") 276 "Field separator character (default TAB).")
248 277
249 (defvar forms-read-only nil 278 (defvar forms-read-only nil
250 "Read-only mode (defaults to the write access on the data file).") 279 "Non-nil means: visit the file in view (read-only) mode.
280 (Defaults to the write access on the data file).")
251 281
252 (defvar forms-multi-line "\C-k" 282 (defvar forms-multi-line "\C-k"
253 "Character to separate multi-line fields (default C-k).") 283 "If not nil: use this character to separate multi-line fields (default C-k).")
254 284
255 (defvar forms-forms-scroll t 285 (defvar forms-forms-scroll t
256 "*Non-nil means replace scroll-up/down commands in Forms mode. 286 "*Non-nil means replace scroll-up/down commands in Forms mode.
257 The replacement commands performs forms-next/prev-record.") 287 The replacement commands performs forms-next/prev-record.")
258 288
259 (defvar forms-forms-jump t 289 (defvar forms-forms-jump t
260 "*Non-nil means redefine beginning/end-of-buffer in Forms mode. 290 "*Non-nil means redefine beginning/end-of-buffer in Forms mode.
261 The replacement commands performs forms-first/last-record.") 291 The replacement commands performs forms-first/last-record.")
292
293 (defvar forms-new-record-filter nil
294 "The name of a function that is called when a new record is created.")
295
296 (defvar forms-modified-record-filter nil
297 "The name of a function that is called when a record has been modified.")
298
299 (defvar forms-fields nil
300 "List with fields of the current forms. First field has number 1.
301 This variable is for use by the filter routines only.
302 The contents may NOT be modified.")
303
304 (defvar forms-use-text-properties (fboundp 'set-text-properties)
305 "*Non-nil means: use emacs-19 text properties.
306 Defaults to t if this emacs is capable of handling text properties.")
307
308 (defvar forms-ro-face 'default
309 "The face (a symbol) that is used to display read-only text on the screen.")
310
311 (defvar forms-rw-face 'region
312 "The face (a symbol) that is used to display read-write text on the screen.")
262 313
263 ;;; Internal variables. 314 ;;; Internal variables.
264 315
265 (defvar forms--file-buffer nil 316 (defvar forms--file-buffer nil
266 "Buffer which holds the file data") 317 "Buffer which holds the file data")
275 "Keymap for form buffer.") 326 "Keymap for form buffer.")
276 327
277 (defvar forms--markers nil 328 (defvar forms--markers nil
278 "Field markers in the screen.") 329 "Field markers in the screen.")
279 330
280 (defvar forms--number-of-markers 0 331 (defvar forms--dyntexts nil
281 "Number of fields on screen.") 332 "Dynamic texts (resulting from function calls) on the screen.")
282 333
283 (defvar forms--the-record-list nil 334 (defvar forms--the-record-list nil
284 "List of strings of the current record, as parsed from the file.") 335 "List of strings of the current record, as parsed from the file.")
285 336
286 (defvar forms--search-regexp nil 337 (defvar forms--search-regexp nil
291 342
292 (defvar forms--parser nil 343 (defvar forms--parser nil
293 "Forms parser routine.") 344 "Forms parser routine.")
294 345
295 (defvar forms--mode-setup nil 346 (defvar forms--mode-setup nil
296 "Internal - keeps track of forms-mode being set-up.") 347 "To keep track of forms-mode being set-up.")
297 (make-variable-buffer-local 'forms--mode-setup) 348 (make-variable-buffer-local 'forms--mode-setup)
298 349
299 (defvar forms--new-record-filter nil 350 (defvar forms--new-record-filter nil
300 "Internal - set if a new record filter has been defined.") 351 "Set if a new record filter has been defined.")
301 352
302 (defvar forms--modified-record-filter nil 353 (defvar forms--modified-record-filter nil
303 "Internal - set if a modified record filter has been defined.") 354 "Set if a modified record filter has been defined.")
304 355
305 (defvar forms--dynamic-text nil 356 (defvar forms--dynamic-text nil
306 "Internal - holds dynamic text to insert between fields.") 357 "Array that holds dynamic texts to insert between fields.")
307 358
308 (defvar forms-fields nil 359 (defvar forms--elements nil
309 "List with fields of the current forms. First field has number 1.") 360 "Array with the order in which the fields are displayed.")
310 361
311 (defvar forms-new-record-filter nil 362 (defvar forms--ro-face nil
312 "The name of a function that is called when a new record is created.") 363 "Face used to represent read-only data on the screen.")
313 364
314 (defvar forms-modified-record-filter nil 365 (defvar forms--rw-face nil
315 "The name of a function that is called when a record has been modified.") 366 "Face used to represent read-write data on the screen.")
316 367
317 ;;; forms-mode
318 ;;;
319 ;;; This is not a simple major mode, as usual. Therefore, forms-mode
320 ;;; takes an optional argument 'primary' which is used for the initial
321 ;;; set-up. Normal use would leave 'primary' to nil.
322 ;;;
323 ;;; A global buffer-local variable 'forms--mode-setup' has the same effect
324 ;;; but makes it possible to auto-invoke forms-mode using find-file.
325 ;;;
326 ;;; Note: although it seems logical to have (make-local-variable) executed
327 ;;; where the variable is first needed, I deliberately placed all calls
328 ;;; in the forms-mode function.
329
330 ;;;###autoload 368 ;;;###autoload
331 (defun forms-mode (&optional primary) 369 (defun forms-mode (&optional primary)
332 "Major mode to visit files in a field-structured manner using a form. 370 "Major mode to visit files in a field-structured manner using a form.
333 371
334 Commands (prefix with C-c if not in read-only mode): 372 Commands (prefix with C-c if not in read-only mode):
335 \\{forms-mode-map}" 373 \\{forms-mode-map}"
336 374
337 (interactive) ; no - 'primary' is not prefix arg 375 (interactive) ; no - 'primary' is not prefix arg
376
377 ;; This is not a simple major mode, as usual. Therefore, forms-mode
378 ;; takes an optional argument `primary' which is used for the
379 ;; initial set-up. Normal use would leave `primary' to nil.
380 ;; A global buffer-local variable `forms--mode-setup' has the same
381 ;; effect but makes it possible to auto-invoke forms-mode using
382 ;; `find-file'.
383 ;; Note: although it seems logical to have `make-local-variable'
384 ;; executed where the variable is first needed, I have deliberately
385 ;; placed all calls in this function.
338 386
339 ;; Primary set-up: evaluate buffer and check if the mandatory 387 ;; Primary set-up: evaluate buffer and check if the mandatory
340 ;; variables have been set. 388 ;; variables have been set.
341 (if (or primary (not forms--mode-setup)) 389 (if (or primary (not forms--mode-setup))
342 (progn 390 (progn
391 ;;(message "forms: setting up...")
343 (kill-all-local-variables) 392 (kill-all-local-variables)
344 393
345 ;; make mandatory variables 394 ;; Make mandatory variables.
346 (make-local-variable 'forms-file) 395 (make-local-variable 'forms-file)
347 (make-local-variable 'forms-number-of-fields) 396 (make-local-variable 'forms-number-of-fields)
348 (make-local-variable 'forms-format-list) 397 (make-local-variable 'forms-format-list)
349 398
350 ;; make optional variables 399 ;; Make optional variables.
351 (make-local-variable 'forms-field-sep) 400 (make-local-variable 'forms-field-sep)
352 (make-local-variable 'forms-read-only) 401 (make-local-variable 'forms-read-only)
353 (make-local-variable 'forms-multi-line) 402 (make-local-variable 'forms-multi-line)
354 (make-local-variable 'forms-forms-scroll) 403 (make-local-variable 'forms-forms-scroll)
355 (make-local-variable 'forms-forms-jump) 404 (make-local-variable 'forms-forms-jump)
405 (make-local-variable 'forms-use-text-properties)
406 (make-local-variable 'forms--new-record-filter)
407 (make-local-variable 'forms--modified-record-filter)
408
409 ;; Make sure no filters exist.
356 (fmakunbound 'forms-new-record-filter) 410 (fmakunbound 'forms-new-record-filter)
411 (fmakunbound 'forms-modified-record-filter)
412
413 ;; If running Emacs 19 under X, setup faces to show read-only and
414 ;; read-write fields.
415 (if (fboundp 'make-face)
416 (progn
417 (make-local-variable 'forms-ro-face)
418 (make-local-variable 'forms-rw-face)))
357 419
358 ;; eval the buffer, should set variables 420 ;; eval the buffer, should set variables
421 ;;(message "forms: processing control file...")
359 (eval-current-buffer) 422 (eval-current-buffer)
360 423
361 ;; check if the mandatory variables make sense. 424 ;; check if the mandatory variables make sense.
362 (or forms-file 425 (or forms-file
363 (error "'forms-file' has not been set")) 426 (error "'forms-file' has not been set"))
371 (if (and (stringp forms-multi-line) 434 (if (and (stringp forms-multi-line)
372 (eq (length forms-multi-line) 1)) 435 (eq (length forms-multi-line) 1))
373 (if (string= forms-multi-line forms-field-sep) 436 (if (string= forms-multi-line forms-field-sep)
374 (error "'forms-multi-line' is equal to 'forms-field-sep'")) 437 (error "'forms-multi-line' is equal to 'forms-field-sep'"))
375 (error "'forms-multi-line' must be nil or a one-character string"))) 438 (error "'forms-multi-line' must be nil or a one-character string")))
439 (or (fboundp 'set-text-properties)
440 (setq forms-use-text-properties nil))
376 441
377 ;; validate and process forms-format-list 442 ;; Validate and process forms-format-list.
378 (make-local-variable 'forms--number-of-markers) 443 ;;(message "forms: pre-processing format list...")
444 (forms--process-format-list)
445
446 ;; Build the formatter and parser.
447 ;;(message "forms: building formatter...")
448 (make-local-variable 'forms--format)
379 (make-local-variable 'forms--markers) 449 (make-local-variable 'forms--markers)
380 (forms--process-format-list) 450 (make-local-variable 'forms--dyntexts)
381 451 (make-local-variable 'forms--elements)
382 ;; build the formatter and parser 452 ;;(message "forms: building parser...")
383 (make-local-variable 'forms--format)
384 (forms--make-format) 453 (forms--make-format)
385 (make-local-variable 'forms--parser) 454 (make-local-variable 'forms--parser)
386 (forms--make-parser) 455 (forms--make-parser)
387 456 ;;(message "forms: building parser... done.")
388 ;; check if record filters are defined 457
389 (make-local-variable 'forms--new-record-filter) 458 ;; Check if record filters are defined.
390 (setq forms--new-record-filter 459 (setq forms--new-record-filter
391 (cond 460 (cond
392 ((fboundp 'forms-new-record-filter) 461 ((fboundp 'forms-new-record-filter)
393 (symbol-function 'forms-new-record-filter)) 462 (symbol-function 'forms-new-record-filter))
394 ((and (boundp 'forms-new-record-filter) 463 ((and (boundp 'forms-new-record-filter)
395 (fboundp forms-new-record-filter)) 464 (fboundp forms-new-record-filter))
396 forms-new-record-filter))) 465 forms-new-record-filter)))
397 (fmakunbound 'forms-new-record-filter) 466 (fmakunbound 'forms-new-record-filter)
398 (make-local-variable 'forms--modified-record-filter)
399 (setq forms--modified-record-filter 467 (setq forms--modified-record-filter
400 (cond 468 (cond
401 ((fboundp 'forms-modified-record-filter) 469 ((fboundp 'forms-modified-record-filter)
402 (symbol-function 'forms-modified-record-filter)) 470 (symbol-function 'forms-modified-record-filter))
403 ((and (boundp 'forms-modified-record-filter) 471 ((and (boundp 'forms-modified-record-filter)
404 (fboundp forms-modified-record-filter)) 472 (fboundp forms-modified-record-filter))
405 forms-modified-record-filter))) 473 forms-modified-record-filter)))
406 (fmakunbound 'forms-modified-record-filter) 474 (fmakunbound 'forms-modified-record-filter)
407 475
408 ;; dynamic text support 476 ;; The filters acces the contents of the forms using `forms-fields'.
477 (make-local-variable 'forms-fields)
478
479 ;; Dynamic text support.
409 (make-local-variable 'forms--dynamic-text) 480 (make-local-variable 'forms--dynamic-text)
410 (make-local-variable 'forms-fields) 481
411 482 ;; Prevent accidental overwrite of the control file and autosave.
412 ;; prepare this buffer for further processing
413 (setq buffer-read-only nil)
414
415 ;; prevent accidental overwrite of the control file and autosave
416 (setq buffer-file-name nil) 483 (setq buffer-file-name nil)
417 (auto-save-mode nil) 484 (auto-save-mode nil)
418 485
419 ;; and clean it 486 ;; Prepare this buffer for further processing.
420 (erase-buffer))) 487 (setq buffer-read-only nil)
488 (erase-buffer)
489
490 ;;(message "forms: setting up... done.")
491 ))
492
493 ;; Copy desired faces to the actual variables used by the forms formatter.
494 (if (fboundp 'make-face)
495 (progn
496 (make-local-variable 'forms--ro-face)
497 (make-local-variable 'forms--rw-face)
498 (if forms-read-only
499 (progn
500 (setq forms--ro-face forms-ro-face)
501 (setq forms--rw-face forms-ro-face))
502 (setq forms--ro-face forms-ro-face)
503 (setq forms--rw-face forms-rw-face))))
421 504
422 ;; Make more local variables. 505 ;; Make more local variables.
423 (make-local-variable 'forms--file-buffer) 506 (make-local-variable 'forms--file-buffer)
424 (make-local-variable 'forms--total-records) 507 (make-local-variable 'forms--total-records)
425 (make-local-variable 'forms--current-record) 508 (make-local-variable 'forms--current-record)
426 (make-local-variable 'forms--the-record-list) 509 (make-local-variable 'forms--the-record-list)
427 (make-local-variable 'forms--search-rexexp) 510 (make-local-variable 'forms--search-regexp)
428 511
429 ;; A bug in the current Emacs release prevents a keymap 512 ;; A bug in the current Emacs release prevents a keymap
430 ;; which is buffer-local from being used by 'describe-mode'. 513 ;; which is buffer-local from being used by 'describe-mode'.
431 ;; Hence we'll leave it global. 514 ;; Hence we'll leave it global.
432 ;;(make-local-variable 'forms-mode-map) 515 ;;(make-local-variable 'forms-mode-map)
433 (if forms-mode-map ; already defined 516 (if forms-mode-map ; already defined
434 nil 517 nil
518 ;;(message "forms: building keymap...")
435 (setq forms-mode-map (make-keymap)) 519 (setq forms-mode-map (make-keymap))
436 (forms--mode-commands forms-mode-map)) 520 (forms--mode-commands forms-mode-map)
521 ;;(message "forms: building keymap... done.")
522 )
437 523
438 ;; find the data file 524 ;; find the data file
439 (setq forms--file-buffer (find-file-noselect forms-file)) 525 (setq forms--file-buffer (find-file-noselect forms-file))
440 526
441 ;; count the number of records, and set see if it may be modified 527 ;; count the number of records, and set see if it may be modified
442 (let (ro) 528 (let (ro)
443 (setq forms--total-records 529 (setq forms--total-records
444 (save-excursion 530 (save-excursion
445 (set-buffer forms--file-buffer) 531 (prog1
446 (bury-buffer (current-buffer)) 532 (progn
447 (setq ro buffer-read-only) 533 ;;(message "forms: counting records...")
448 (count-lines (point-min) (point-max)))) 534 (set-buffer forms--file-buffer)
535 (bury-buffer (current-buffer))
536 (setq ro buffer-read-only)
537 (count-lines (point-min) (point-max)))
538 ;;(message "forms: counting records... done.")
539 )))
449 (if ro 540 (if ro
450 (setq forms-read-only t))) 541 (setq forms-read-only t)))
451 542
543 ;;(message "forms: proceeding setup...")
452 ;; set the major mode indicator 544 ;; set the major mode indicator
453 (setq major-mode 'forms-mode) 545 (setq major-mode 'forms-mode)
454 (setq mode-name "Forms") 546 (setq mode-name "Forms")
455 (make-local-variable 'minor-mode-alist) ; needed? 547 (make-local-variable 'minor-mode-alist) ; needed?
548 ;;(message "forms: proceeding setup (minor mode)...")
456 (forms--set-minor-mode) 549 (forms--set-minor-mode)
550 ;;(message "forms: proceeding setup (keymaps)...")
457 (forms--set-keymaps) 551 (forms--set-keymaps)
458 (make-local-variable 'local-write-file-hooks) 552 (make-local-variable 'local-write-file-hooks)
553 ;;(message "forms: proceeding setup (commands)...")
459 (forms--change-commands) 554 (forms--change-commands)
460 555
556 ;;(message "forms: proceeding setup (buffer)...")
461 (set-buffer-modified-p nil) 557 (set-buffer-modified-p nil)
462 558
463 ;; We have our own revert function - use it 559 ;; We have our own revert function - use it
464 (make-local-variable 'revert-buffer-function) 560 (make-local-variable 'revert-buffer-function)
465 (setq revert-buffer-function 'forms-revert-buffer) 561 (setq revert-buffer-function 'forms-revert-buffer)
468 (if (< forms--current-record 1) 564 (if (< forms--current-record 1)
469 (setq forms--current-record 1)) 565 (setq forms--current-record 1))
470 (forms-jump-record forms--current-record) 566 (forms-jump-record forms--current-record)
471 567
472 ;; user customising 568 ;; user customising
569 ;;(message "forms: proceeding setup (user hooks)...")
473 (run-hooks 'forms-mode-hooks) 570 (run-hooks 'forms-mode-hooks)
571 ;;(message "forms: setting up... done.")
474 572
475 ;; be helpful 573 ;; be helpful
476 (forms--help) 574 (forms--help)
477 575
478 ;; initialization done 576 ;; initialization done
479 (setq forms--mode-setup t)) 577 (setq forms--mode-setup t))
480 578
481 ;;; forms-process-format-list
482 ;;;
483 ;;; Validates forms-format-list.
484 ;;; Sets forms--number-of-markers and forms--markers.
485
486 (defun forms--process-format-list () 579 (defun forms--process-format-list ()
487 "Validate forms-format-list and set some global variables." 580 ;; Validate `forms-format-list' and set some global variables.
488 581 ;; Symbols in the list are evaluated, and consecutive strings are
489 (forms--debug "forms-forms-list before 1st pass:\n" 582 ;; concatenated.
490 'forms-format-list) 583 ;; Array `forms--elements' is constructed that contains the order
491 584 ;; of the fields on the display. This array is used by
492 ;; it must be non-nil 585 ;; `forms--parser-using-text-properties' to extract the fields data
586 ;; from the form on the screen.
587 ;; Upon completion, `forms-format-list' is garanteed correct, so
588 ;; `forms--make-format' and `forms--make-parser' do not need to perform
589 ;; any checks.
590
591 ;; Verify that `forms-format-list' is not nil.
493 (or forms-format-list 592 (or forms-format-list
494 (error "'forms-format-list' has not been set")) 593 (error "'forms-format-list' has not been set"))
495 ;; it must be a list ... 594 ;; It must be a list.
496 (or (listp forms-format-list) 595 (or (listp forms-format-list)
497 (error "'forms-format-list' is not a list")) 596 (error "'forms-format-list' is not a list"))
498 597
499 (setq forms--number-of-markers 0) 598 ;; Assume every field is painted once.
599 ;; `forms--elements' will grow if needed.
600 (setq forms--elements (make-vector forms-number-of-fields nil))
500 601
501 (let ((the-list forms-format-list) ; the list of format elements 602 (let ((the-list forms-format-list) ; the list of format elements
502 (this-item 0) ; element in list 603 (this-item 0) ; element in list
604 (prev-item nil)
503 (field-num 0)) ; highest field number 605 (field-num 0)) ; highest field number
504 606
505 (setq forms-format-list nil) ; gonna rebuild 607 (setq forms-format-list nil) ; gonna rebuild
506 608
507 (while the-list 609 (while the-list
508 610
509 (let ((el (car-safe the-list)) 611 (let ((el (car-safe the-list))
510 (rem (cdr-safe the-list))) 612 (rem (cdr-safe the-list)))
511 613
512 ;; if it is a symbol, eval it first 614 ;; If it is a symbol, eval it first.
513 (if (and (symbolp el) 615 (if (and (symbolp el)
514 (boundp el)) 616 (boundp el))
515 (setq el (eval el))) 617 (setq el (eval el)))
516 618
517 (cond 619 (cond
518 620
519 ;; try string ... 621 ;; Try string ...
520 ((stringp el)) ; string is OK 622 ((stringp el)
521 623 (if (stringp prev-item) ; try to concatenate strings
522 ;; try numeric ... 624 (setq prev-item (concat prev-item el))
625 (if prev-item
626 (setq forms-format-list
627 (append forms-format-list (list prev-item) nil)))
628 (setq prev-item el)))
629
630 ;; Try numeric ...
523 ((numberp el) 631 ((numberp el)
524 632
633 ;; Validate range.
525 (if (or (<= el 0) 634 (if (or (<= el 0)
526 (> el forms-number-of-fields)) 635 (> el forms-number-of-fields))
527 (error 636 (error
528 "Forms error: field number %d out of range 1..%d" 637 "Forms error: field number %d out of range 1..%d"
529 el forms-number-of-fields)) 638 el forms-number-of-fields))
530 639
531 (setq forms--number-of-markers (1+ forms--number-of-markers)) 640 ;; Store forms order.
532 (if (> el field-num) 641 (if (> field-num (length forms--elements))
533 (setq field-num el))) 642 (setq forms--elements (vconcat forms--elements (1- el)))
534 643 (aset forms--elements field-num (1- el)))
535 ;; try function 644 (setq field-num (1+ field-num))
645
646 ;; Make sure the field is preceded by something.
647 (if prev-item
648 (setq forms-format-list
649 (append forms-format-list (list prev-item) nil))
650 (setq forms-format-list
651 (append forms-format-list (list "\n") nil)))
652 (setq prev-item el))
653
654 ;; Try function ...
536 ((listp el) 655 ((listp el)
656
657 ;; Validate.
537 (or (fboundp (car-safe el)) 658 (or (fboundp (car-safe el))
538 (error 659 (error
539 "Forms error: not a function: %s" 660 "Forms error: not a function: %s"
540 (prin1-to-string (car-safe el))))) 661 (prin1-to-string (car-safe el))))
662
663 ;; Shift.
664 (if prev-item
665 (setq forms-format-list
666 (append forms-format-list (list prev-item) nil)))
667 (setq prev-item el))
541 668
542 ;; else 669 ;; else
543 (t 670 (t
544 (error "Invalid element in 'forms-format-list': %s" 671 (error "Forms error: invalid element %s"
545 (prin1-to-string el)))) 672 (prin1-to-string el))))
546 673
547 ;; advance to next element of the list 674 ;; Advance to next element of the list.
548 (setq the-list rem) 675 (setq the-list rem)))
549 (setq forms-format-list 676
550 (append forms-format-list (list el) nil))))) 677 ;; Append last item.
551 678 (if prev-item
552 (forms--debug "forms-forms-list after 1st pass:\n" 679 (progn
553 'forms-format-list) 680 (setq forms-format-list
554 681 (append forms-format-list (list prev-item) nil))
555 ;; concat adjacent strings 682 ;; Append a newline if the last item is a field.
556 (setq forms-format-list (forms--concat-adjacent forms-format-list)) 683 ;; This prevents pasrsing problems.
557 684 ;; Also it makes it possible to insert an empty last field.
558 (forms--debug "forms-forms-list after 2nd pass:\n" 685 (if (numberp prev-item)
559 'forms-format-list 686 (setq forms-format-list
560 'forms--number-of-markers) 687 (append forms-format-list (list "\n") nil))))))
561 688
562 (setq forms--markers (make-vector forms--number-of-markers nil))) 689 (forms--debug 'forms-format-list
690 'forms--elements))
563 691
564 ;;; Build the format routine from forms-format-list. 692 ;; Special treatment for read-only segments.
565 ;;; 693 ;;
566 ;;; The format routine (forms--format) will look like 694 ;; If text is inserted after a read-only segment, it inherits the
567 ;;; 695 ;; read-only properties. This is not what we want.
568 ;;; (lambda (arg) 696 ;; The modification hook of the last character of the read-only segment
569 ;;; (setq forms--dynamic-text nil) 697 ;; temporarily switches its properties to read-write, so the new
570 ;;; ;; "text: " 698 ;; text gets the right properties.
571 ;;; (insert "text: ") 699 ;; The post-command-hook is used to restore the original properties.
572 ;;; ;; 6 700 ;;
573 ;;; (aset forms--markers 0 (point-marker)) 701 ;; A character category `forms-electric' is used for the characters
574 ;;; (insert (elt arg 5)) 702 ;; that get the modification hook set. Using a category, it is
575 ;;; ;; "\nmore text: " 703 ;; possible to globally enable/disable the modification hook. This is
576 ;;; (insert "\nmore text: ") 704 ;; necessary, since modifying a hook or setting text properties are
577 ;;; ;; (tocol 40) 705 ;; considered modifications and would trigger the hooks while building
578 ;;; (let ((the-dyntext (tocol 40))) 706 ;; the forms.
579 ;;; (insert the-dyntext) 707
580 ;;; (setq forms--dynamic-text (append forms--dynamic-text 708 (defvar forms--ro-modification-start nil
581 ;;; (list the-dyntext)))) 709 "Record start of modification command.")
582 ;;; ;; 9 710 (defvar forms--ro-properties nil
583 ;;; (aset forms--markers 1 (point-marker)) 711 "Original properties of the character being overridden.")
584 ;;; (insert (elt arg 8)) 712
585 ;;; 713 (defun forms--romh (begin end)
586 ;;; ... ) 714 "`modification-hook' function for forms-electric characters."
587 ;;; 715
716 ;; Note start location.
717 (or forms--ro-modification-start
718 (setq forms--ro-modification-start (point)))
719
720 ;; Fetch current properties.
721 (setq forms--ro-properties
722 (text-properties-at (1- forms--ro-modification-start)))
723
724 ;; Disarm modification hook.
725 (setplist 'forms--electric nil)
726
727 ;; Replace them.
728 (let ((inhibit-read-only t))
729 (set-text-properties
730 (1- forms--ro-modification-start) forms--ro-modification-start
731 (list 'face forms--rw-face)))
732
733 ;; Re-arm electric.
734 (setplist 'forms--electric '(modification-hooks (forms--romh)))
735
736 ;; Enable `post-command-hook' to restore the properties.
737 (setq post-command-hook
738 (append (list 'forms--romh-post-command-hook) post-command-hook)))
739
740 (defun forms--romh-post-command-hook ()
741 "`post-command-hook' function for forms--electric characters."
742
743 ;; Disable `post-command-hook'.
744 (setq post-command-hook
745 (delq 'forms--romh-post-command-hook post-command-hook))
746
747 ;; Disarm modification hook.
748 (setplist 'forms--electric nil)
749
750 ;; Restore properties.
751 (if forms--ro-modification-start
752 (let ((inhibit-read-only t))
753 (set-text-properties
754 (1- forms--ro-modification-start) forms--ro-modification-start
755 forms--ro-properties)))
756
757 ;; Re-arm electric.
758 (setplist 'forms--electric '(modification-hooks (forms--romh)))
759
760 ;; Cleanup.
761 (setq forms--ro-modification-start nil))
762
763 (defvar forms--marker)
764 (defvar forms--dyntext)
588 765
589 (defun forms--make-format () 766 (defun forms--make-format ()
590 "Generate format function for forms." 767 "Generate `forms--format' using the information in `forms-format-list'."
591 (setq forms--format (forms--format-maker forms-format-list)) 768
769 ;; The real work is done using a mapcar of `forms--make-format-elt' on
770 ;; `forms-format-list'.
771 ;; This function sets up the necessary environment, and decides
772 ;; which function to mapcar.
773
774 (let ((forms--marker 0)
775 (forms--dyntext 0))
776 (setq
777 forms--format
778 (if forms-use-text-properties
779 (` (lambda (arg)
780 (let ((inhibit-read-only t))
781 (setplist 'forms--electric nil)
782 (,@ (apply 'append
783 (mapcar 'forms--make-format-elt-using-text-properties
784 forms-format-list))))
785 (setplist 'forms--electric
786 '(modification-hooks (forms--romh)))
787 (setq forms--ro-modification-start nil)))
788 (` (lambda (arg)
789 (,@ (apply 'append
790 (mapcar 'forms--make-format-elt forms-format-list)))))))
791
792 ;; We have tallied the number of markers and dynamic texts,
793 ;; so we can allocate the arrays now.
794 (setq forms--markers (make-vector forms--marker nil))
795 (setq forms--dyntexts (make-vector forms--dyntext nil)))
592 (forms--debug 'forms--format)) 796 (forms--debug 'forms--format))
593 797
594 (defun forms--format-maker (the-format-list) 798 (defun forms--make-format-elt-using-text-properties (el)
595 "Returns the parser function for forms." 799 "Helper routine to generate format function."
596 (let ((the-marker 0)) 800
597 (` (lambda (arg) 801 ;; The format routine `forms--format' will look like
598 (setq forms--dynamic-text nil) 802 ;;
599 (,@ (apply 'append 803 ;; ;; preamble
600 (mapcar 'forms--make-format-elt the-format-list))))))) 804 ;; (lambda (arg)
805 ;; (let ((inhibit-read-only t))
806 ;; (setplist 'forms--electric nil)
807 ;;
808 ;; ;; a string, e.g. "text: "
809 ;; (set-text-properties
810 ;; (point)
811 ;; (progn (insert "text: ") (point))
812 ;; (list 'face forms--ro-face 'read-only 1))
813 ;;
814 ;; ;; a field, e.g. 6
815 ;; (let ((here (point)))
816 ;; (aset forms--markers 0 (point-marker))
817 ;; (insert (elt arg 5))
818 ;; (or (= (point) here)
819 ;; (set-text-properties
820 ;; here (point)
821 ;; (list 'face forms--rw-face)))
822 ;; (if (get-text-property (1- here) 'read-only)
823 ;; (put-text-property
824 ;; (1- here) here
825 ;; 'category 'forms--electric)))
826 ;;
827 ;; ;; another string, e.g. "\nmore text: "
828 ;; (set-text-properties
829 ;; (point)
830 ;; (progn (insert "\nmore text: ") (point))
831 ;; (list 'face forms--ro-face
832 ;; 'read-only 2))
833 ;;
834 ;; ;; a function, e.g. (tocol 40)
835 ;; (set-text-properties
836 ;; (point)
837 ;; (progn
838 ;; (insert (aset forms--dyntexts 0 (tocol 40)))
839 ;; (point))
840 ;; (list 'face forms--ro-face
841 ;; 'read-only 2))
842 ;;
843 ;; ;; wrap up
844 ;; (setplist 'forms--electric
845 ;; '(modification-hooks (forms--romh)))
846 ;; (setq forms--ro-modification-start nil)
847 ;; ))
848
849 (cond
850 ((stringp el)
851
852 (` ((set-text-properties
853 (point) ; start at point
854 (progn ; until after insertion
855 (insert (, el))
856 (point))
857 (list 'face forms--ro-face ; read-only appearance
858 'read-only (,@ (list (1+ forms--marker))))))))
859 ((numberp el)
860 (` ((let ((here (point)))
861 (aset forms--markers
862 (, (prog1 forms--marker
863 (setq forms--marker (1+ forms--marker))))
864 (point-marker))
865 (insert (elt arg (, (1- el))))
866 (or (= (point) here)
867 (set-text-properties
868 here (point)
869 (list 'face forms--rw-face)))
870 (if (get-text-property (1- here) 'read-only)
871 (put-text-property
872 (1- here) here
873 'category 'forms--electric))))))
874
875 ((listp el)
876 (` ((set-text-properties
877 (point)
878 (progn
879 (insert (aset forms--dyntexts
880 (, (prog1 forms--dyntext
881 (setq forms--dyntext (1+ forms--dyntext))))
882 (, el)))
883 (point))
884 (list 'face forms--ro-face
885 'read-only
886 (,@ (list (1+ forms--marker))))))))
887
888 ;; end of cond
889 ))
601 890
602 (defun forms--make-format-elt (el) 891 (defun forms--make-format-elt (el)
892 "Helper routine to generate format function."
893
894 ;; If we're not using text properties, the format routine
895 ;; `forms--format' will look like
896 ;;
897 ;; (lambda (arg)
898 ;; ;; a string, e.g. "text: "
899 ;; (insert "text: ")
900 ;; ;; a field, e.g. 6
901 ;; (aset forms--markers 0 (point-marker))
902 ;; (insert (elt arg 5))
903 ;; ;; another string, e.g. "\nmore text: "
904 ;; (insert "\nmore text: ")
905 ;; ;; a function, e.g. (tocol 40)
906 ;; (insert (aset forms--dyntexts 0 (tocol 40)))
907 ;; ... )
908
603 (cond 909 (cond
604 ((stringp el) 910 ((stringp el)
605 (` ((insert (, el))))) 911 (` ((insert (, el)))))
606 ((numberp el) 912 ((numberp el)
607 (prog1 913 (prog1
608 (` ((aset forms--markers (, the-marker) (point-marker)) 914 (` ((aset forms--markers (, forms--marker) (point-marker))
609 (insert (elt arg (, (1- el)))))) 915 (insert (elt arg (, (1- el))))))
610 (setq the-marker (1+ the-marker)))) 916 (setq forms--marker (1+ forms--marker))))
611 ((listp el) 917 ((listp el)
612 (prog1 918 (prog1
613 (` ((let ((the-dyntext (, el))) 919 (` ((insert (aset forms--dyntexts (, forms--dyntext) (, el)))))
614 (insert the-dyntext) 920 (setq forms--dyntext (1+ forms--dyntext))))))
615 (setq forms--dynamic-text (append forms--dynamic-text
616 (list the-dyntext)))))
617 )))))
618
619 (defun forms--concat-adjacent (the-list)
620 "Concatenate adjacent strings in the-list and return the resulting list."
621 (if (consp the-list)
622 (let ((the-rest (forms--concat-adjacent (cdr the-list))))
623 (if (and (stringp (car the-list)) (stringp (car the-rest)))
624 (cons (concat (car the-list) (car the-rest))
625 (cdr the-rest))
626 (cons (car the-list) the-rest)))
627 the-list))
628 921
629 ;;; forms--make-parser. 922 (defvar forms--field)
630 ;;; 923 (defvar forms--recordv)
631 ;;; Generate parse routine from forms-format-list. 924 (defvar forms--seen-text)
632 ;;;
633 ;;; The parse routine (forms--parser) will look like (give or take
634 ;;; a few " " .
635 ;;;
636 ;;; (lambda nil
637 ;;; (let (here)
638 ;;; (goto-char (point-min))
639 ;;;
640 ;;; ;; "text: "
641 ;;; (if (not (looking-at "text: "))
642 ;;; (error "Parse error: cannot find \"text: \""))
643 ;;; (forward-char 6) ; past "text: "
644 ;;;
645 ;;; ;; 6
646 ;;; ;; "\nmore text: "
647 ;;; (setq here (point))
648 ;;; (if (not (search-forward "\nmore text: " nil t nil))
649 ;;; (error "Parse error: cannot find \"\\nmore text: \""))
650 ;;; (aset the-recordv 5 (buffer-substring here (- (point) 12)))
651 ;;;
652 ;;; ;; (tocol 40)
653 ;;; (let ((the-dyntext (car-safe forms--dynamic-text)))
654 ;;; (if (not (looking-at (regexp-quote the-dyntext)))
655 ;;; (error "Parse error: not looking at \"%s\"" the-dyntext))
656 ;;; (forward-char (length the-dyntext))
657 ;;; (setq forms--dynamic-text (cdr-safe forms--dynamic-text)))
658 ;;; ...
659 ;;; ;; final flush (due to terminator sentinel, see below)
660 ;;; (aset the-recordv 7 (buffer-substring (point) (point-max)))
661 ;;;
662 925
663 (defun forms--make-parser () 926 (defun forms--make-parser ()
664 "Generate parser function for forms." 927 "Generate `forms--parser' from the information in `forms-format-list'."
665 (setq forms--parser (forms--parser-maker forms-format-list)) 928
929 ;; If we can use text properties, we simply set it to
930 ;; `forms--parser-using-text-properties'.
931 ;; Otherwise, the function is constructed using a mapcar of
932 ;; `forms--make-parser-elt on `forms-format-list'.
933
934 (setq
935 forms--parser
936 (if forms-use-text-properties
937 (function forms--parser-using-text-properties)
938 (let ((forms--field nil)
939 (forms--seen-text nil)
940 (forms--dyntext 0))
941
942 ;; Note: we add a nil element to the list passed to `mapcar',
943 ;; see `forms--make-parser-elt' for details.
944 (` (lambda nil
945 (let (here)
946 (goto-char (point-min))
947 (,@ (apply 'append
948 (mapcar
949 'forms--make-parser-elt
950 (append forms-format-list (list nil)))))))))))
951
666 (forms--debug 'forms--parser)) 952 (forms--debug 'forms--parser))
667 953
668 (defun forms--parser-maker (the-format-list) 954 (defun forms--parser-using-text-properties ()
669 "Returns the parser function for forms." 955 "Extract field info from forms when using text properties."
670 (let ((the-field nil) 956
671 (seen-text nil) 957 ;; Using text properties, we can simply jump to the markers, and
672 the--format-list) 958 ;; extract the information up to the following read-only segment.
673 ;; add a terminator sentinel 959
674 (setq the--format-list (append the-format-list (list nil))) 960 (let ((i 0)
675 (` (lambda nil 961 here there)
676 (let (here) 962 (while (< i (length forms--markers))
677 (goto-char (point-min)) 963 (goto-char (setq here (aref forms--markers i)))
678 (,@ (apply 'append 964 (if (get-text-property here 'read-only)
679 (mapcar 'forms--make-parser-elt the--format-list)))))))) 965 (aset forms--recordv (aref forms--elements i) nil)
966 (if (setq there
967 (next-single-property-change here 'read-only))
968 (aset forms--recordv (aref forms--elements i)
969 (buffer-substring here there))
970 (aset forms--recordv (aref forms--elements i)
971 (buffer-substring here (point-max)))))
972 (setq i (1+ i)))))
680 973
681 (defun forms--make-parser-elt (el) 974 (defun forms--make-parser-elt (el)
975 "Helper routine to generate forms parser function."
976
977 ;; The parse routine will look like:
978 ;;
979 ;; (lambda nil
980 ;; (let (here)
981 ;; (goto-char (point-min))
982 ;;
983 ;; ;; "text: "
984 ;; (if (not (looking-at "text: "))
985 ;; (error "Parse error: cannot find \"text: \""))
986 ;; (forward-char 6) ; past "text: "
987 ;;
988 ;; ;; 6
989 ;; ;; "\nmore text: "
990 ;; (setq here (point))
991 ;; (if (not (search-forward "\nmore text: " nil t nil))
992 ;; (error "Parse error: cannot find \"\\nmore text: \""))
993 ;; (aset forms--recordv 5 (buffer-substring here (- (point) 12)))
994 ;;
995 ;; ;; (tocol 40)
996 ;; (let ((forms--dyntext (car-safe forms--dynamic-text)))
997 ;; (if (not (looking-at (regexp-quote forms--dyntext)))
998 ;; (error "Parse error: not looking at \"%s\"" forms--dyntext))
999 ;; (forward-char (length forms--dyntext))
1000 ;; (setq forms--dynamic-text (cdr-safe forms--dynamic-text)))
1001 ;; ...
1002 ;; ;; final flush (due to terminator sentinel, see below)
1003 ;; (aset forms--recordv 7 (buffer-substring (point) (point-max)))
1004
682 (cond 1005 (cond
683 ((stringp el) 1006 ((stringp el)
684 (prog1 1007 (prog1
685 (if the-field 1008 (if forms--field
686 (` ((setq here (point)) 1009 (` ((setq here (point))
687 (if (not (search-forward (, el) nil t nil)) 1010 (if (not (search-forward (, el) nil t nil))
688 (error "Parse error: cannot find \"%s\"" (, el))) 1011 (error "Parse error: cannot find \"%s\"" (, el)))
689 (aset the-recordv (, (1- the-field)) 1012 (aset forms--recordv (, (1- forms--field))
690 (buffer-substring here 1013 (buffer-substring here
691 (- (point) (, (length el))))))) 1014 (- (point) (, (length el)))))))
692 (` ((if (not (looking-at (, (regexp-quote el)))) 1015 (` ((if (not (looking-at (, (regexp-quote el))))
693 (error "Parse error: not looking at \"%s\"" (, el))) 1016 (error "Parse error: not looking at \"%s\"" (, el)))
694 (forward-char (, (length el)))))) 1017 (forward-char (, (length el))))))
695 (setq seen-text t) 1018 (setq forms--seen-text t)
696 (setq the-field nil))) 1019 (setq forms--field nil)))
697 ((numberp el) 1020 ((numberp el)
698 (if the-field 1021 (if forms--field
699 (error "Cannot parse adjacent fields %d and %d" 1022 (error "Cannot parse adjacent fields %d and %d"
700 the-field el) 1023 forms--field el)
701 (setq the-field el) 1024 (setq forms--field el)
702 nil)) 1025 nil))
703 ((null el) 1026 ((null el)
704 (if the-field 1027 (if forms--field
705 (` ((aset the-recordv (, (1- the-field)) 1028 (` ((aset forms--recordv (, (1- forms--field))
706 (buffer-substring (point) (point-max))))))) 1029 (buffer-substring (point) (point-max)))))))
707 ((listp el) 1030 ((listp el)
708 (prog1 1031 (prog1
709 (if the-field 1032 (if forms--field
710 (` ((let ((here (point)) 1033 (` ((let ((here (point))
711 (the-dyntext (car-safe forms--dynamic-text))) 1034 (forms--dyntext (aref forms--dyntexts (, forms--dyntext))))
712 (if (not (search-forward the-dyntext nil t nil)) 1035 (if (not (search-forward forms--dyntext nil t nil))
713 (error "Parse error: cannot find \"%s\"" the-dyntext)) 1036 (error "Parse error: cannot find \"%s\"" forms--dyntext))
714 (aset the-recordv (, (1- the-field)) 1037 (aset forms--recordv (, (1- forms--field))
715 (buffer-substring here 1038 (buffer-substring here
716 (- (point) (length the-dyntext)))) 1039 (- (point) (length forms--dyntext)))))))
717 (setq forms--dynamic-text (cdr-safe forms--dynamic-text))))) 1040 (` ((let ((forms--dyntext (aref forms--dyntexts (, forms--dyntext))))
718 (` ((let ((the-dyntext (car-safe forms--dynamic-text))) 1041 (if (not (looking-at (regexp-quote forms--dyntext)))
719 (if (not (looking-at (regexp-quote the-dyntext))) 1042 (error "Parse error: not looking at \"%s\"" forms--dyntext))
720 (error "Parse error: not looking at \"%s\"" the-dyntext)) 1043 (forward-char (length forms--dyntext))))))
721 (forward-char (length the-dyntext)) 1044 (setq forms--dyntext (1+ forms--dyntext))
722 (setq forms--dynamic-text (cdr-safe forms--dynamic-text)))))) 1045 (setq forms--seen-text t)
723 (setq seen-text t) 1046 (setq forms--field nil)))
724 (setq the-field nil)))
725 )) 1047 ))
726 1048
727 (defun forms--set-minor-mode () 1049 (defun forms--set-minor-mode ()
728 (setq minor-mode-alist 1050 (setq minor-mode-alist
729 (if forms-read-only 1051 (if forms-read-only
739 (define-key (current-local-map) "\C-c" forms-mode-map) 1061 (define-key (current-local-map) "\C-c" forms-mode-map)
740 (define-key (current-local-map) "\t" 'forms-next-field))) 1062 (define-key (current-local-map) "\t" 'forms-next-field)))
741 1063
742 (defun forms--mode-commands (map) 1064 (defun forms--mode-commands (map)
743 "Fill map with all Forms mode commands." 1065 "Fill map with all Forms mode commands."
1066
744 (define-key map "\t" 'forms-next-field) 1067 (define-key map "\t" 'forms-next-field)
745 (define-key map " " 'forms-next-record) 1068 (define-key map " " 'forms-next-record)
746 (define-key map "d" 'forms-delete-record) 1069 (define-key map "d" 'forms-delete-record)
747 (define-key map "e" 'forms-edit-mode) 1070 (define-key map "e" 'forms-edit-mode)
748 (define-key map "i" 'forms-insert-record) 1071 (define-key map "i" 'forms-insert-record)
755 (define-key map "x" 'forms-exit-no-save) 1078 (define-key map "x" 'forms-exit-no-save)
756 (define-key map "<" 'forms-first-record) 1079 (define-key map "<" 'forms-first-record)
757 (define-key map ">" 'forms-last-record) 1080 (define-key map ">" 'forms-last-record)
758 (define-key map "?" 'describe-mode) 1081 (define-key map "?" 'describe-mode)
759 (define-key map "\177" 'forms-prev-record) 1082 (define-key map "\177" 'forms-prev-record)
760 ; (define-key map "\C-c" map) 1083 ;(define-key map "\C-c" map)
761 (define-key map "\e" 'ESC-prefix) 1084 ;(define-key map "\e" 'ESC-prefix)
762 (define-key map "\C-x" ctl-x-map) 1085 ;(define-key map "\C-x" ctl-x-map)
763 (define-key map "\C-u" 'universal-argument) 1086 ;(define-key map "\C-u" 'universal-argument)
764 (define-key map "\C-h" help-map) 1087 ;(define-key map "\C-h" help-map)
765 ) 1088 )
766 1089
767 ;;; Changed functions 1090 ;;; Changed functions
768 1091
769 (defun forms--change-commands () 1092 (defun forms--change-commands ()
770 "Localize some commands for Forms mode." 1093 "Localize some commands for Forms mode."
771 ;; 1094
772 ;; scroll-down -> forms-prev-record 1095 ;; scroll-down -> forms-prev-record
773 ;; scroll-up -> forms-next-record 1096 ;; scroll-up -> forms-next-record
774 (if forms-forms-scroll 1097 (if forms-forms-scroll
775 (progn 1098 (progn
776 (substitute-key-definition 'scroll-up 'forms-next-record 1099 (substitute-key-definition 'scroll-up 'forms-next-record
826 (while (setq i (string-match re subj i)) 1149 (while (setq i (string-match re subj i))
827 (aset subj i k) 1150 (aset subj i k)
828 (setq i (1+ i))))) 1151 (setq i (1+ i)))))
829 1152
830 (defun forms--exit (query &optional save) 1153 (defun forms--exit (query &optional save)
1154 "Internal exit from forms mode function."
1155
831 (let ((buf (buffer-name forms--file-buffer))) 1156 (let ((buf (buffer-name forms--file-buffer)))
832 (forms--checkmod) 1157 (forms--checkmod)
833 (if (and save 1158 (if (and save
834 (buffer-modified-p forms--file-buffer)) 1159 (buffer-modified-p forms--file-buffer))
835 (save-excursion 1160 (save-excursion
847 (delete-auto-save-file-if-necessary) 1172 (delete-auto-save-file-if-necessary)
848 (kill-buffer (current-buffer))))) 1173 (kill-buffer (current-buffer)))))
849 1174
850 (defun forms--get-record () 1175 (defun forms--get-record ()
851 "Fetch the current record from the file buffer." 1176 "Fetch the current record from the file buffer."
852 ;; 1177
853 ;; This function is executed in the context of the forms--file-buffer. 1178 ;; This function is executed in the context of the `forms--file-buffer'.
854 ;; 1179
855 (or (bolp) 1180 (or (bolp)
856 (beginning-of-line nil)) 1181 (beginning-of-line nil))
857 (let ((here (point))) 1182 (let ((here (point)))
858 (prog2 1183 (prog2
859 (end-of-line) 1184 (end-of-line)
861 (goto-char here)))) 1186 (goto-char here))))
862 1187
863 (defun forms--show-record (the-record) 1188 (defun forms--show-record (the-record)
864 "Format THE-RECORD and display it in the current buffer." 1189 "Format THE-RECORD and display it in the current buffer."
865 1190
866 ;; split the-record 1191 ;; Split the-record.
867 (let (the-result 1192 (let (the-result
868 (start-pos 0) 1193 (start-pos 0)
869 found-pos 1194 found-pos
870 (field-sep-length (length forms-field-sep))) 1195 (field-sep-length (length forms-field-sep)))
871 (if forms-multi-line 1196 (if forms-multi-line
872 (forms--trans the-record forms-multi-line "\n")) 1197 (forms--trans the-record forms-multi-line "\n"))
873 ;; add an extra separator (makes splitting easy) 1198 ;; Add an extra separator (makes splitting easy).
874 (setq the-record (concat the-record forms-field-sep)) 1199 (setq the-record (concat the-record forms-field-sep))
875 (while (setq found-pos (string-match forms-field-sep the-record start-pos)) 1200 (while (setq found-pos (string-match forms-field-sep the-record start-pos))
876 (let ((ent (substring the-record start-pos found-pos))) 1201 (let ((ent (substring the-record start-pos found-pos)))
877 (setq the-result 1202 (setq the-result
878 (append the-result (list ent))) 1203 (append the-result (list ent)))
879 (setq start-pos (+ field-sep-length found-pos)))) 1204 (setq start-pos (+ field-sep-length found-pos))))
880 (setq forms--the-record-list the-result)) 1205 (setq forms--the-record-list the-result))
881 1206
882 (setq buffer-read-only nil) 1207 (setq buffer-read-only nil)
1208 (if forms-use-text-properties
1209 (let ((inhibit-read-only t))
1210 (setplist 'forms--electric nil)
1211 (set-text-properties (point-min) (point-max) nil)))
883 (erase-buffer) 1212 (erase-buffer)
884 1213
885 ;; verify the number of fields, extend forms--the-record-list if needed 1214 ;; Verify the number of fields, extend forms--the-record-list if needed.
886 (if (= (length forms--the-record-list) forms-number-of-fields) 1215 (if (= (length forms--the-record-list) forms-number-of-fields)
887 nil 1216 nil
888 (beep) 1217 (beep)
889 (message "Record has %d fields instead of %d." 1218 (message "Record has %d fields instead of %d."
890 (length forms--the-record-list) forms-number-of-fields) 1219 (length forms--the-record-list) forms-number-of-fields)
894 (make-list 1223 (make-list
895 (- forms-number-of-fields 1224 (- forms-number-of-fields
896 (length forms--the-record-list)) 1225 (length forms--the-record-list))
897 ""))))) 1226 "")))))
898 1227
899 ;; call the formatter function 1228 ;; Call the formatter function.
900 (setq forms-fields (append (list nil) forms--the-record-list nil)) 1229 (setq forms-fields (append (list nil) forms--the-record-list nil))
901 (funcall forms--format forms--the-record-list) 1230 (funcall forms--format forms--the-record-list)
902 1231
903 ;; prepare 1232 ;; Prepare.
904 (goto-char (point-min)) 1233 (goto-char (point-min))
905 (set-buffer-modified-p nil) 1234 (set-buffer-modified-p nil)
906 (setq buffer-read-only forms-read-only) 1235 (setq buffer-read-only forms-read-only)
907 (setq mode-line-process 1236 (setq mode-line-process
908 (concat " " forms--current-record "/" forms--total-records))) 1237 (concat " " forms--current-record "/" forms--total-records)))
914 ;; A vector with the strings from the original record is 1243 ;; A vector with the strings from the original record is
915 ;; constructed, which is updated with the new contents. Therefore 1244 ;; constructed, which is updated with the new contents. Therefore
916 ;; fields which were not in the form are not modified. 1245 ;; fields which were not in the form are not modified.
917 ;; Finally, the vector is transformed into a list for further processing. 1246 ;; Finally, the vector is transformed into a list for further processing.
918 1247
919 (let (the-recordv) 1248 (let (forms--recordv)
920 1249
921 ;; build the vector 1250 ;; Build the vector.
922 (setq the-recordv (vconcat forms--the-record-list)) 1251 (setq forms--recordv (vconcat forms--the-record-list))
923 1252
924 ;; parse the form and update the vector 1253 ;; Parse the form and update the vector.
925 (let ((forms--dynamic-text forms--dynamic-text)) 1254 (let ((forms--dynamic-text forms--dynamic-text))
926 (funcall forms--parser)) 1255 (funcall forms--parser))
927 1256
928 (if forms--modified-record-filter 1257 (if forms--modified-record-filter
929 ;; As a service to the user, we add a zeroth element so she 1258 ;; As a service to the user, we add a zeroth element so she
930 ;; can use the same indices as in the forms definition. 1259 ;; can use the same indices as in the forms definition.
931 (let ((the-fields (vconcat [nil] the-recordv))) 1260 (let ((the-fields (vconcat [nil] forms--recordv)))
932 (setq the-fields (funcall forms--modified-record-filter the-fields)) 1261 (setq the-fields (funcall forms--modified-record-filter the-fields))
933 (cdr (append the-fields nil))) 1262 (cdr (append the-fields nil)))
934 1263
935 ;; transform to a list and return 1264 ;; Transform to a list and return.
936 (append the-recordv nil)))) 1265 (append forms--recordv nil))))
937 1266
938 (defun forms--update () 1267 (defun forms--update ()
939 "Update current record with contents of form. 1268 "Update current record with contents of form.
940 As a side effect: sets forms--the-record-list ." 1269 As a side effect: sets `forms--the-record-list'."
941 1270
942 (if forms-read-only 1271 (if forms-read-only
943 (progn 1272 (progn
944 (message "Read-only buffer!") 1273 (message "Read-only buffer!")
945 (beep)) 1274 (beep))
946 1275
947 (let (the-record) 1276 (let (the-record)
948 ;; build new record 1277 ;; Build new record.
949 (setq forms--the-record-list (forms--parse-form)) 1278 (setq forms--the-record-list (forms--parse-form))
950 (setq the-record 1279 (setq the-record
951 (mapconcat 'identity forms--the-record-list forms-field-sep)) 1280 (mapconcat 'identity forms--the-record-list forms-field-sep))
952 1281
953 ;; handle multi-line fields, if allowed 1282 ;; Handle multi-line fields, if allowed.
954 (if forms-multi-line 1283 (if forms-multi-line
955 (forms--trans the-record "\n" forms-multi-line)) 1284 (forms--trans the-record "\n" forms-multi-line))
956 1285
957 ;; a final sanity check before updating 1286 ;; A final sanity check before updating.
958 (if (string-match "\n" the-record) 1287 (if (string-match "\n" the-record)
959 (progn 1288 (progn
960 (message "Multi-line fields in this record - update refused!") 1289 (message "Multi-line fields in this record - update refused!")
961 (beep)) 1290 (beep))
962 1291
1019 1348
1020 (defun forms-jump-record (arg &optional relative) 1349 (defun forms-jump-record (arg &optional relative)
1021 "Jump to a random record." 1350 "Jump to a random record."
1022 (interactive "NRecord number: ") 1351 (interactive "NRecord number: ")
1023 1352
1024 ;; verify that the record number is within range 1353 ;; Verify that the record number is within range.
1025 (if (or (> arg forms--total-records) 1354 (if (or (> arg forms--total-records)
1026 (<= arg 0)) 1355 (<= arg 0))
1027 (progn 1356 (progn
1028 (beep) 1357 (beep)
1029 ;; don't give the message if just paging 1358 ;; Don't give the message if just paging.
1030 (if (not relative) 1359 (if (not relative)
1031 (message "Record number %d out of range 1..%d" 1360 (message "Record number %d out of range 1..%d"
1032 arg forms--total-records)) 1361 arg forms--total-records))
1033 ) 1362 )
1034 1363
1035 ;; flush 1364 ;; Flush.
1036 (forms--checkmod) 1365 (forms--checkmod)
1037 1366
1038 ;; calculate displacement 1367 ;; Calculate displacement.
1039 (let ((disp (- arg forms--current-record)) 1368 (let ((disp (- arg forms--current-record))
1040 (cur forms--current-record)) 1369 (cur forms--current-record))
1041 1370
1042 ;; forms--show-record needs it now 1371 ;; `forms--show-record' needs it now.
1043 (setq forms--current-record arg) 1372 (setq forms--current-record arg)
1044 1373
1045 ;; get the record and show it 1374 ;; Get the record and show it.
1046 (forms--show-record 1375 (forms--show-record
1047 (save-excursion 1376 (save-excursion
1048 (set-buffer forms--file-buffer) 1377 (set-buffer forms--file-buffer)
1049 (beginning-of-line) 1378 (beginning-of-line)
1050 1379
1051 ;; move, and adjust the amount if needed (shouldn't happen) 1380 ;; Move, and adjust the amount if needed (shouldn't happen).
1052 (if relative 1381 (if relative
1053 (if (zerop disp) 1382 (if (zerop disp)
1054 nil 1383 nil
1055 (setq cur (+ cur disp (- (forward-line disp))))) 1384 (setq cur (+ cur disp (- (forward-line disp)))))
1056 (setq cur (+ cur disp (- (goto-line arg))))) 1385 (setq cur (+ cur disp (- (goto-line arg)))))
1057 1386
1058 (forms--get-record))) 1387 (forms--get-record)))
1059 1388
1060 ;; this shouldn't happen 1389 ;; This shouldn't happen.
1061 (if (/= forms--current-record cur) 1390 (if (/= forms--current-record cur)
1062 (progn 1391 (progn
1063 (setq forms--current-record cur) 1392 (setq forms--current-record cur)
1064 (beep) 1393 (beep)
1065 (message "Stuck at record %d." cur)))))) 1394 (message "Stuck at record %d." cur))))))
1121 ;; (setq forms-new-record-filter 'my-new-record-filter) 1450 ;; (setq forms-new-record-filter 'my-new-record-filter)
1122 1451
1123 (defun forms-insert-record (arg) 1452 (defun forms-insert-record (arg)
1124 "Create a new record before the current one. 1453 "Create a new record before the current one.
1125 With ARG: store the record after the current one. 1454 With ARG: store the record after the current one.
1126 If a function forms-new-record-filter is defined, or 1455 If a function `forms-new-record-filter' is defined, or
1127 forms-new-record-filter contains the name of a function, 1456 `forms-new-record-filter' contains the name of a function,
1128 it is called to fill (some of) the fields with default values." 1457 it is called to fill (some of) the fields with default values."
1129 ; The above doc is not true, but for documentary purposes only 1458 ; The above doc is not true, but for documentary purposes only
1130 1459
1131 (interactive "P") 1460 (interactive "P")
1132 1461
1230 (if (zerop arg) 1559 (if (zerop arg)
1231 (setq cnt 1) 1560 (setq cnt 1)
1232 (setq cnt (+ cnt arg))) 1561 (setq cnt (+ cnt arg)))
1233 1562
1234 (if (catch 'done 1563 (if (catch 'done
1235 (while (< i forms--number-of-markers) 1564 (while (< i (length forms--markers))
1236 (if (or (null (setq there (aref forms--markers i))) 1565 (if (or (null (setq there (aref forms--markers i)))
1237 (<= there here)) 1566 (<= there here))
1238 nil 1567 nil
1239 (if (<= (setq cnt (1- cnt)) 0) 1568 (if (<= (setq cnt (1- cnt)) 0)
1240 (progn 1569 (progn
1286 (if (fboundp el) 1615 (if (fboundp el)
1287 (setq ret (concat ret (prin1-to-string (symbol-function el)) 1616 (setq ret (concat ret (prin1-to-string (symbol-function el))
1288 "\n")))))) 1617 "\n"))))))
1289 (save-excursion 1618 (save-excursion
1290 (set-buffer (get-buffer-create "*forms-mode debug*")) 1619 (set-buffer (get-buffer-create "*forms-mode debug*"))
1620 (if (zerop (buffer-size))
1621 (emacs-lisp-mode))
1291 (goto-char (point-max)) 1622 (goto-char (point-max))
1292 (insert ret))))) 1623 (insert ret)))))
1293 1624
1294 ;;; forms.el ends here. 1625 ;;; forms.el ends here.