diff options
| author | Richard M. Stallman | 1993-07-17 19:15:19 +0000 |
|---|---|---|
| committer | Richard M. Stallman | 1993-07-17 19:15:19 +0000 |
| commit | fbee972730ad516a6c638a2e5e153964e2a9f080 (patch) | |
| tree | 0ec90041a177407ccb2ebd08ccc2c05465e98686 | |
| parent | b5ae9ca5a96b2845d7c1695879bf2f1df830398c (diff) | |
| download | emacs-fbee972730ad516a6c638a2e5e153964e2a9f080.tar.gz emacs-fbee972730ad516a6c638a2e5e153964e2a9f080.zip | |
Rewritten by Vromans to use text properties.
| -rw-r--r-- | lisp/forms.el | 969 |
1 files changed, 650 insertions, 319 deletions
diff --git a/lisp/forms.el b/lisp/forms.el index e6fced291a5..e476e4eabdc 100644 --- a/lisp/forms.el +++ b/lisp/forms.el | |||
| @@ -2,8 +2,7 @@ | |||
| 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 | ||
| @@ -33,16 +32,16 @@ | |||
| 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 | ;;; |
| @@ -54,41 +53,43 @@ | |||
| 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: | ||
| 69 | ;;; | 67 | ;;; |
| 70 | ;;; forms-file [string] the name of the data file. | 68 | ;;; forms-file [string] |
| 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] |
| 75 | ;;; Formatting instructions. | ||
| 76 | ;;; | 76 | ;;; |
| 77 | ;;; The forms-format-list should be a list, each element containing | 77 | ;;; `forms-format-list' should be a list, each element containing |
| 78 | ;;; | 78 | ;;; |
| 79 | ;;; - a string, e.g. "hello" (which is inserted \"as is\"), | 79 | ;;; - a string, e.g. "hello". The string is inserted in the forms |
| 80 | ;;; | 80 | ;;; "as is". |
| 81 | ;;; - an integer, denoting a field number. The contents of the field | 81 | ;;; |
| 82 | ;;; are inserted at this point. | 82 | ;;; - an integer, denoting a field number. |
| 83 | ;;; The first field has number one. | 83 | ;;; The contents of this field are inserted at this point. |
| 84 | ;;; | 84 | ;;; Fields are numbered starting with number one. |
| 85 | ;;; - a function call, e.g. (insert "text"). This function call is | 85 | ;;; |
| 86 | ;;; dynamically evaluated and should return a string. It should *NOT* | 86 | ;;; - a function call, e.g. (insert "text"). |
| 87 | ;;; have side-effects on the forms being constructed. | 87 | ;;; This function call is dynamically evaluated and should return a |
| 88 | ;;; The current fields are available to the function in the variable | 88 | ;;; string. It should *NOT* have side-effects on the forms being |
| 89 | ;;; forms-fields, they should *NOT* be modified. | 89 | ;;; constructed. The current fields are available to the function |
| 90 | ;;; | 90 | ;;; in the variable `forms-fields', they should *NOT* be modified. |
| 91 | ;;; - a lisp symbol, that must evaluate to one of the above. | 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 | ;;; |
| @@ -97,28 +98,30 @@ | |||
| 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 |
| @@ -137,33 +140,59 @@ | |||
| 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 |
| 151 | ;;; the records. | 178 | ;;; the records according to `forms-format-list'. |
| 152 | ;;; | 179 | ;;; |
| 153 | ;;; When a form is changed the record is updated as soon as this form | 180 | ;;; When a form is changed the record is updated as soon as this form |
| 154 | ;;; is left. The contents of the form are parsed using forms-format-list, | 181 | ;;; is left. The contents of the form are parsed using information |
| 155 | ;;; and the fields which are deduced from the form are modified. So, | 182 | ;;; obtained from `forms-format-list', and the fields which are |
| 156 | ;;; fields not shown on the forms retain their origional values. | 183 | ;;; deduced from the form are modified. Fields not shown on the forms |
| 157 | ;;; The newly formed record and replaces the contents of the | 184 | ;;; retain their origional values. The newly formed record then |
| 158 | ;;; old record in forms--file-buffer. | 185 | ;;; replaces the contents of the old record in `forms--file-buffer'. |
| 159 | ;;; A parse routine 'forms--parser' is built upon startup to parse | 186 | ;;; A parse routine `forms--parser' is built upon startup to parse |
| 160 | ;;; the records. | 187 | ;;; the records. |
| 161 | ;;; | 188 | ;;; |
| 162 | ;;; Two exit functions exist: forms-exit (which saves) and forms-exit-no-save | 189 | ;;; Two exit functions exist: `forms-exit' and `forms-exit-no-save'. |
| 163 | ;;; (which doesn't). However, if forms-exit-no-save is executed and the file | 190 | ;;; `forms-exit' saves the data to the file, if modified. |
| 164 | ;;; buffer has been modified, emacs will ask questions. | 191 | ;;; `forms-exit-no-save` does not. However, if `forms-exit-no-save' |
| 192 | ;;; is executed and the file buffer has been modified, Emacs will ask | ||
| 193 | ;;; questions anyway. | ||
| 165 | ;;; | 194 | ;;; |
| 166 | ;;; Other functions are: | 195 | ;;; Other functions provided by forms mode 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) |
| @@ -179,9 +208,10 @@ | |||
| 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 | ;;; |
| @@ -203,34 +233,33 @@ | |||
| 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 | ||
| 211 | ;;; rebinding. | ||
| 212 | ;;; | 244 | ;;; |
| 213 | ;;; A local-write-file hook is defined to save the actual data file | 245 | ;;; `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 | 246 | ;;; instead of the buffer data, `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.") |
| @@ -241,16 +270,17 @@ | |||
| 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. |
| @@ -259,6 +289,27 @@ The replacement commands performs forms-next/prev-record.") | |||
| 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 | ||
| @@ -277,8 +328,8 @@ The replacement commands performs forms-first/last-record.") | |||
| 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.") |
| @@ -293,40 +344,27 @@ The replacement commands performs forms-first/last-record.") | |||
| 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. |
| @@ -336,26 +374,51 @@ Commands (prefix with C-c if not in read-only mode): | |||
| 336 | 374 | ||
| 337 | (interactive) ; no - 'primary' is not prefix arg | 375 | (interactive) ; no - 'primary' is not prefix arg |
| 338 | 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. | ||
| 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. |
| @@ -373,20 +436,26 @@ Commands (prefix with C-c if not in read-only mode): | |||
| 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...") |
| 379 | (make-local-variable 'forms--markers) | ||
| 380 | (forms--process-format-list) | 444 | (forms--process-format-list) |
| 381 | 445 | ||
| 382 | ;; build the formatter and parser | 446 | ;; Build the formatter and parser. |
| 447 | ;;(message "forms: building formatter...") | ||
| 383 | (make-local-variable 'forms--format) | 448 | (make-local-variable 'forms--format) |
| 449 | (make-local-variable 'forms--markers) | ||
| 450 | (make-local-variable 'forms--dyntexts) | ||
| 451 | (make-local-variable 'forms--elements) | ||
| 452 | ;;(message "forms: building parser...") | ||
| 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) |
| 456 | ;;(message "forms: building parser... done.") | ||
| 387 | 457 | ||
| 388 | ;; check if record filters are defined | 458 | ;; Check if record filters are defined. |
| 389 | (make-local-variable 'forms--new-record-filter) | ||
| 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) |
| @@ -395,7 +464,6 @@ Commands (prefix with C-c if not in read-only mode): | |||
| 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) |
| @@ -405,26 +473,41 @@ Commands (prefix with C-c if not in read-only mode): | |||
| 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'. |
| 409 | (make-local-variable 'forms--dynamic-text) | ||
| 410 | (make-local-variable 'forms-fields) | 477 | (make-local-variable 'forms-fields) |
| 411 | 478 | ||
| 412 | ;; prepare this buffer for further processing | 479 | ;; Dynamic text support. |
| 413 | (setq buffer-read-only nil) | 480 | (make-local-variable 'forms--dynamic-text) |
| 414 | 481 | ||
| 415 | ;; prevent accidental overwrite of the control file and autosave | 482 | ;; 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'. |
| @@ -432,8 +515,11 @@ Commands (prefix with C-c if not in read-only mode): | |||
| 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)) |
| @@ -442,22 +528,32 @@ Commands (prefix with C-c if not in read-only mode): | |||
| 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 |
| @@ -470,7 +566,9 @@ Commands (prefix with C-c if not in read-only mode): | |||
| 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) |
| @@ -478,28 +576,32 @@ Commands (prefix with C-c if not in read-only mode): | |||
| 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 |
| @@ -509,219 +611,439 @@ Commands (prefix with C-c if not in read-only mode): | |||
| 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 | ||
| 550 | (append forms-format-list (list el) nil))))) | ||
| 551 | |||
| 552 | (forms--debug "forms-forms-list after 1st pass:\n" | ||
| 553 | 'forms-format-list) | ||
| 554 | 676 | ||
| 555 | ;; concat adjacent strings | 677 | ;; Append last item. |
| 556 | (setq forms-format-list (forms--concat-adjacent forms-format-list)) | 678 | (if prev-item |
| 557 | 679 | (progn | |
| 558 | (forms--debug "forms-forms-list after 2nd pass:\n" | 680 | (setq forms-format-list |
| 559 | 'forms-format-list | 681 | (append forms-format-list (list prev-item) nil)) |
| 560 | 'forms--number-of-markers) | 682 | ;; Append a newline if the last item is a field. |
| 561 | 683 | ;; This prevents pasrsing problems. | |
| 562 | (setq forms--markers (make-vector forms--number-of-markers nil))) | 684 | ;; Also it makes it possible to insert an empty last field. |
| 685 | (if (numberp prev-item) | ||
| 686 | (setq forms-format-list | ||
| 687 | (append forms-format-list (list "\n") nil)))))) | ||
| 688 | |||
| 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 () |
| @@ -741,6 +1063,7 @@ Commands (prefix with C-c if not in read-only mode): | |||
| 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) |
| @@ -757,18 +1080,18 @@ Commands (prefix with C-c if not in read-only mode): | |||
| 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 |
| @@ -828,6 +1151,8 @@ Commands (prefix with C-c if not in read-only mode): | |||
| 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 |
| @@ -849,9 +1174,9 @@ Commands (prefix with C-c if not in read-only mode): | |||
| 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))) |
| @@ -863,14 +1188,14 @@ Commands (prefix with C-c if not in read-only mode): | |||
| 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))) |
| @@ -880,9 +1205,13 @@ Commands (prefix with C-c if not in read-only mode): | |||
| 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) |
| @@ -896,11 +1225,11 @@ Commands (prefix with C-c if not in read-only mode): | |||
| 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) |
| @@ -916,28 +1245,28 @@ Commands (prefix with C-c if not in read-only mode): | |||
| 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 |
| @@ -945,16 +1274,16 @@ As a side effect: sets forms--the-record-list ." | |||
| 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!") |
| @@ -1021,34 +1350,34 @@ As a side effect: sets forms--the-record-list ." | |||
| 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 |
| @@ -1057,7 +1386,7 @@ As a side effect: sets forms--the-record-list ." | |||
| 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) |
| @@ -1123,8 +1452,8 @@ As a side effect: re-calculates the number of records in the data file." | |||
| 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 | ||
| @@ -1232,7 +1561,7 @@ it is called to fill (some of) the fields with default values." | |||
| 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 |
| @@ -1288,6 +1617,8 @@ Usage: (setq forms-number-of-fields | |||
| 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 | ||