Mercurial > emacs
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. |