* doc/emacs/ack.texi (Acknowledgments): Use @Tex{} in more places.
[bpt/emacs.git] / lisp / abbrev.el
CommitLineData
ba83908c 1;;; abbrev.el --- abbrev mode commands for Emacs -*- lexical-binding: t -*-
e2fbf49d 2
acaf905b 3;; Copyright (C) 1985-1987, 1992, 2001-2012 Free Software Foundation, Inc.
e2fbf49d 4
6228c05b 5;; Maintainer: FSF
f5f727f8 6;; Keywords: abbrev convenience
bd78fa1d 7;; Package: emacs
e9571d2a 8
e2fbf49d
JB
9;; This file is part of GNU Emacs.
10
eb3fa2cf 11;; GNU Emacs is free software: you can redistribute it and/or modify
e2fbf49d 12;; it under the terms of the GNU General Public License as published by
eb3fa2cf
GM
13;; the Free Software Foundation, either version 3 of the License, or
14;; (at your option) any later version.
e2fbf49d
JB
15
16;; GNU Emacs is distributed in the hope that it will be useful,
17;; but WITHOUT ANY WARRANTY; without even the implied warranty of
18;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
19;; GNU General Public License for more details.
20
21;; You should have received a copy of the GNU General Public License
eb3fa2cf 22;; along with GNU Emacs. If not, see <http://www.gnu.org/licenses/>.
e2fbf49d 23
e41b2db1
ER
24;;; Commentary:
25
26;; This facility is documented in the Emacs Manual.
27
e047f448
SM
28;; Todo:
29
e047f448
SM
30;; - Cleanup name space.
31
e5167999 32;;; Code:
e2fbf49d 33
e047f448
SM
34(eval-when-compile (require 'cl))
35
36(defgroup abbrev-mode nil
37 "Word abbreviations mode."
38 :link '(custom-manual "(emacs)Abbrevs")
39 :group 'abbrev)
40
2aa0e5bf
SM
41(defcustom abbrev-file-name
42 (locate-user-emacs-file "abbrev_defs" ".abbrev_defs")
790d0270
SM
43 "Default name of file from which to read abbrevs."
44 :initialize 'custom-initialize-delay
2aa0e5bf
SM
45 :type 'file)
46
7cfedc97 47(defcustom only-global-abbrevs nil
e7fdaf63 48 "Non-nil means user plans to use global abbrevs only.
1d1e35a0
RS
49This makes the commands that normally define mode-specific abbrevs
50define global abbrevs instead."
51 :type 'boolean
f5f727f8
DN
52 :group 'abbrev-mode
53 :group 'convenience)
e2fbf49d 54
497afe07 55(define-minor-mode abbrev-mode
38002bff 56 "Toggle Abbrev mode in the current buffer.
06e21633
CY
57With a prefix argument ARG, enable Abbrev mode if ARG is
58positive, and disable it otherwise. If called from Lisp, enable
59Abbrev mode if ARG is omitted or nil.
60
61In Abbrev mode, inserting an abbreviation causes it to expand and
62be replaced by its expansion."
9d794026
GM
63 ;; It's defined in C, this stops the d-m-m macro defining it again.
64 :variable abbrev-mode)
1d1e35a0 65
f31b1257 66(put 'abbrev-mode 'safe-local-variable 'booleanp)
1d1e35a0 67
e2fbf49d 68\f
e7fdaf63
JPW
69(defvar edit-abbrevs-map
70 (let ((map (make-sparse-keymap)))
eb7ffc14
LL
71 (define-key map "\C-x\C-s" 'abbrev-edit-save-buffer)
72 (define-key map "\C-x\C-w" 'abbrev-edit-save-to-file)
e7fdaf63
JPW
73 (define-key map "\C-c\C-c" 'edit-abbrevs-redefine)
74 map)
38002bff 75 "Keymap used in `edit-abbrevs'.")
e2fbf49d
JB
76
77(defun kill-all-abbrevs ()
78 "Undefine all defined abbrevs."
79 (interactive)
0b281d03
SM
80 (dolist (tablesym abbrev-table-name-list)
81 (clear-abbrev-table (symbol-value tablesym))))
e2fbf49d 82
ad9d51b2
RS
83(defun copy-abbrev-table (table)
84 "Make a new abbrev-table with the same abbrevs as TABLE."
85 (let ((new-table (make-abbrev-table)))
86 (mapatoms
87 (lambda (symbol)
88 (define-abbrev new-table
89 (symbol-name symbol)
90 (symbol-value symbol)
91 (symbol-function symbol)))
92 table)
93 new-table))
94
e2fbf49d
JB
95(defun insert-abbrevs ()
96 "Insert after point a description of all defined abbrevs.
97Mark is set after the inserted text."
98 (interactive)
99 (push-mark
100 (save-excursion
0b281d03
SM
101 (dolist (tablesym abbrev-table-name-list)
102 (insert-abbrev-table-description tablesym t))
71baa28f 103 (point))))
e2fbf49d 104
c88a9944
GM
105(defun list-abbrevs (&optional local)
106 "Display a list of defined abbrevs.
107If LOCAL is non-nil, interactively when invoked with a
108prefix arg, display only local, i.e. mode-specific, abbrevs.
109Otherwise display all abbrevs."
110 (interactive "P")
111 (display-buffer (prepare-abbrev-list-buffer local)))
112
113(defun abbrev-table-name (table)
114 "Value is the name of abbrev table TABLE."
115 (let ((tables abbrev-table-name-list)
116 found)
117 (while (and (not found) tables)
118 (when (eq (symbol-value (car tables)) table)
119 (setq found (car tables)))
120 (setq tables (cdr tables)))
121 found))
7cfedc97 122
c88a9944 123(defun prepare-abbrev-list-buffer (&optional local)
8d4708af
BG
124 (let ((local-table local-abbrev-table))
125 (with-current-buffer (get-buffer-create "*Abbrevs*")
126 (erase-buffer)
127 (if local
128 (insert-abbrev-table-description
129 (abbrev-table-name local-table) t)
947b6566
LL
130 (let (empty-tables)
131 (dolist (table abbrev-table-name-list)
132 (if (abbrev-table-empty-p (symbol-value table))
133 (push table empty-tables)
134 (insert-abbrev-table-description table t)))
135 (dolist (table (nreverse empty-tables))
136 (insert-abbrev-table-description table t))))
8d4708af
BG
137 (goto-char (point-min))
138 (set-buffer-modified-p nil)
139 (edit-abbrevs-mode)
140 (current-buffer))))
e2fbf49d
JB
141
142(defun edit-abbrevs-mode ()
143 "Major mode for editing the list of abbrev definitions.
144\\{edit-abbrevs-map}"
145 (interactive)
1a96d1f3 146 (kill-all-local-variables)
e2fbf49d
JB
147 (setq major-mode 'edit-abbrevs-mode)
148 (setq mode-name "Edit-Abbrevs")
1a96d1f3
LK
149 (use-local-map edit-abbrevs-map)
150 (run-mode-hooks 'edit-abbrevs-mode-hook))
e2fbf49d
JB
151
152(defun edit-abbrevs ()
153 "Alter abbrev definitions by editing a list of them.
154Selects a buffer containing a list of abbrev definitions.
155You can edit them and type \\<edit-abbrevs-map>\\[edit-abbrevs-redefine] to redefine abbrevs
156according to your editing.
157Buffer contains a header line for each abbrev table,
158 which is the abbrev table name in parentheses.
159This is followed by one line per abbrev in that table:
160NAME USECOUNT EXPANSION HOOK
161where NAME and EXPANSION are strings with quotes,
162USECOUNT is an integer, and HOOK is any valid function
163or may be omitted (it is usually omitted)."
164 (interactive)
c3313451 165 (switch-to-buffer (prepare-abbrev-list-buffer)))
e2fbf49d
JB
166
167(defun edit-abbrevs-redefine ()
168 "Redefine abbrevs according to current buffer contents."
169 (interactive)
4cabf12b
RS
170 (save-restriction
171 (widen)
172 (define-abbrevs t)
173 (set-buffer-modified-p nil)))
e2fbf49d
JB
174
175(defun define-abbrevs (&optional arg)
176 "Define abbrevs according to current visible buffer contents.
177See documentation of `edit-abbrevs' for info on the format of the
178text you must have in the buffer.
179With argument, eliminate all abbrev definitions except
180the ones defined from the buffer now."
181 (interactive "P")
182 (if arg (kill-all-abbrevs))
183 (save-excursion
71baa28f
EZ
184 (goto-char (point-min))
185 (while (and (not (eobp)) (re-search-forward "^(" nil t))
186 (let* ((buf (current-buffer))
187 (table (read buf))
188 abbrevs name hook exp count sys)
189 (forward-line 1)
190 (while (progn (forward-line 1)
191 (not (eolp)))
192 (setq name (read buf) count (read buf))
193 (if (equal count '(sys))
194 (setq sys t count (read buf)))
195 (setq exp (read buf))
196 (skip-chars-backward " \t\n\f")
197 (setq hook (if (not (eolp)) (read buf)))
198 (skip-chars-backward " \t\n\f")
199 (setq abbrevs (cons (list name exp hook count sys) abbrevs)))
200 (define-abbrev-table table abbrevs)))))
e2fbf49d
JB
201
202(defun read-abbrev-file (&optional file quietly)
203 "Read abbrev definitions from file written with `write-abbrev-file'.
204Optional argument FILE is the name of the file to read;
205it defaults to the value of `abbrev-file-name'.
d0b6d945 206Optional second argument QUIETLY non-nil means don't display a message."
4cabf12b
RS
207 (interactive
208 (list
209 (read-file-name (format "Read abbrev file (default %s): "
210 abbrev-file-name)
211 nil abbrev-file-name t)))
ec7793c3 212 (load (or file abbrev-file-name) nil quietly)
d0b6d945 213 (setq abbrevs-changed nil))
e2fbf49d
JB
214
215(defun quietly-read-abbrev-file (&optional file)
e7fdaf63 216 "Read abbrev definitions from file written with `write-abbrev-file'.
e2fbf49d
JB
217Optional argument FILE is the name of the file to read;
218it defaults to the value of `abbrev-file-name'.
d0b6d945 219Does not display any message."
71baa28f 220 ;(interactive "fRead abbrev file: ")
e2fbf49d
JB
221 (read-abbrev-file file t))
222
eb7ffc14 223(defun write-abbrev-file (&optional file verbose)
68063965
LT
224 "Write all user-level abbrev definitions to a file of Lisp code.
225This does not include system abbrevs; it includes only the abbrev tables
226listed in listed in `abbrev-table-name-list'.
e2fbf49d 227The file written can be loaded in another session to define the same abbrevs.
68063965 228The argument FILE is the file name to write. If omitted or nil, the file
eb7ffc14
LL
229specified in `abbrev-file-name' is used.
230If VERBOSE is non-nil, display a message indicating where abbrevs
231have been saved."
e2fbf49d
JB
232 (interactive
233 (list
234 (read-file-name "Write abbrev file: "
235 (file-name-directory (expand-file-name abbrev-file-name))
236 abbrev-file-name)))
e2fbf49d
JB
237 (or (and file (> (length file) 0))
238 (setq file abbrev-file-name))
4b978a67
LL
239 (let ((coding-system-for-write 'utf-8))
240 (with-temp-buffer
71baa28f 241 (dolist (table
4b978a67
LL
242 ;; We sort the table in order to ease the automatic
243 ;; merging of different versions of the user's abbrevs
244 ;; file. This is useful, for example, for when the
245 ;; user keeps their home directory in a revision
246 ;; control system, and is therefore keeping multiple
247 ;; slightly-differing copies loosely synchronized.
248 (sort (copy-sequence abbrev-table-name-list)
249 (lambda (s1 s2)
250 (string< (symbol-name s1)
251 (symbol-name s2)))))
252 (insert-abbrev-table-description table nil))
253 (when (unencodable-char-position (point-min) (point-max) 'utf-8)
254 (setq coding-system-for-write
255 (if (> emacs-major-version 24)
256 'utf-8-emacs
257 ;; For compatibility with Emacs 22 (See Bug#8308)
258 'emacs-mule)))
259 (goto-char (point-min))
260 (insert (format ";;-*-coding: %s;-*-\n" coding-system-for-write))
eb7ffc14
LL
261 (write-region nil nil file nil (and (not verbose) 0)))))
262
263(defun abbrev-edit-save-to-file (file)
264 "Save all user-level abbrev definitions in current buffer to FILE."
265 (interactive
266 (list (read-file-name "Save abbrevs to file: "
267 (file-name-directory
268 (expand-file-name abbrev-file-name))
269 abbrev-file-name)))
270 (edit-abbrevs-redefine)
271 (write-abbrev-file file t))
272
273(defun abbrev-edit-save-buffer ()
274 "Save all user-level abbrev definitions in current buffer.
275The saved abbrevs are written to the file specified by
276`abbrev-file-name'."
277 (interactive)
278 (abbrev-edit-save-to-file abbrev-file-name))
279
e2fbf49d
JB
280\f
281(defun add-mode-abbrev (arg)
282 "Define mode-specific abbrev for last word(s) before point.
283Argument is how many words before point form the expansion;
284or zero means the region is the expansion.
285A negative argument means to undefine the specified abbrev.
286Reads the abbreviation in the minibuffer.
287
288Don't use this function in a Lisp program; use `define-abbrev' instead."
289 (interactive "p")
290 (add-abbrev
291 (if only-global-abbrevs
71296446 292 global-abbrev-table
e2fbf49d
JB
293 (or local-abbrev-table
294 (error "No per-mode abbrev table")))
295 "Mode" arg))
296
297(defun add-global-abbrev (arg)
298 "Define global (all modes) abbrev for last word(s) before point.
299The prefix argument specifies the number of words before point that form the
300expansion; or zero means the region is the expansion.
301A negative argument means to undefine the specified abbrev.
302This command uses the minibuffer to read the abbreviation.
303
304Don't use this function in a Lisp program; use `define-abbrev' instead."
305 (interactive "p")
306 (add-abbrev global-abbrev-table "Global" arg))
307
308(defun add-abbrev (table type arg)
309 (let ((exp (and (>= arg 0)
34f3cd03 310 (buffer-substring-no-properties
e2fbf49d
JB
311 (point)
312 (if (= arg 0) (mark)
313 (save-excursion (forward-word (- arg)) (point))))))
314 name)
315 (setq name
316 (read-string (format (if exp "%s abbrev for \"%s\": "
317 "Undefine %s abbrev: ")
318 type exp)))
a0f88464 319 (set-text-properties 0 (length name) nil name)
e2fbf49d
JB
320 (if (or (null exp)
321 (not (abbrev-expansion name table))
322 (y-or-n-p (format "%s expands to \"%s\"; redefine? "
323 name (abbrev-expansion name table))))
324 (define-abbrev table (downcase name) exp))))
7cfedc97 325
e66b273f 326(defun inverse-add-mode-abbrev (n)
e2fbf49d
JB
327 "Define last word before point as a mode-specific abbrev.
328With prefix argument N, defines the Nth word before point.
329This command uses the minibuffer to read the expansion.
330Expands the abbreviation after defining it."
331 (interactive "p")
332 (inverse-add-abbrev
333 (if only-global-abbrevs
7cfedc97 334 global-abbrev-table
e2fbf49d
JB
335 (or local-abbrev-table
336 (error "No per-mode abbrev table")))
e66b273f 337 "Mode" n))
e2fbf49d 338
e66b273f 339(defun inverse-add-global-abbrev (n)
e2fbf49d
JB
340 "Define last word before point as a global (mode-independent) abbrev.
341With prefix argument N, defines the Nth word before point.
342This command uses the minibuffer to read the expansion.
343Expands the abbreviation after defining it."
344 (interactive "p")
e66b273f 345 (inverse-add-abbrev global-abbrev-table "Global" n))
e2fbf49d
JB
346
347(defun inverse-add-abbrev (table type arg)
46684068 348 (let (name exp start end)
e2fbf49d 349 (save-excursion
46684068
GM
350 (forward-word (1+ (- arg)))
351 (setq end (point))
352 (backward-word 1)
353 (setq start (point)
354 name (buffer-substring-no-properties start end)))
355
356 (setq exp (read-string (format "%s expansion for \"%s\": " type name)
357 nil nil nil t))
358 (when (or (not (abbrev-expansion name table))
359 (y-or-n-p (format "%s expands to \"%s\"; redefine? "
360 name (abbrev-expansion name table))))
361 (define-abbrev table (downcase name) exp)
362 (save-excursion
363 (goto-char end)
364 (expand-abbrev)))))
e2fbf49d
JB
365
366(defun abbrev-prefix-mark (&optional arg)
367 "Mark current point as the beginning of an abbrev.
368Abbrev to be expanded starts here rather than at beginning of word.
369This way, you can expand an abbrev with a prefix: insert the prefix,
68063965 370use this command, then insert the abbrev. This command inserts a
29c48340 371temporary hyphen after the prefix (until the intended abbrev
68063965
LT
372expansion occurs).
373If the prefix is itself an abbrev, this command expands it, unless
374ARG is non-nil. Interactively, ARG is the prefix argument."
e2fbf49d
JB
375 (interactive "P")
376 (or arg (expand-abbrev))
377 (setq abbrev-start-location (point-marker)
378 abbrev-start-location-buffer (current-buffer))
379 (insert "-"))
380
381(defun expand-region-abbrevs (start end &optional noquery)
382 "For abbrev occurrence in the region, offer to expand it.
e66b273f
JB
383The user is asked to type `y' or `n' for each occurrence.
384A prefix argument means don't query; expand all abbrevs."
e2fbf49d
JB
385 (interactive "r\nP")
386 (save-excursion
387 (goto-char start)
388 (let ((lim (- (point-max) end))
389 pnt string)
390 (while (and (not (eobp))
391 (progn (forward-word 1)
392 (<= (setq pnt (point)) (- (point-max) lim))))
393 (if (abbrev-expansion
394 (setq string
34f3cd03 395 (buffer-substring-no-properties
e2fbf49d
JB
396 (save-excursion (forward-word -1) (point))
397 pnt)))
398 (if (or noquery (y-or-n-p (format "Expand `%s'? " string)))
399 (expand-abbrev)))))))
c0274f38 400
e047f448
SM
401;;; Abbrev properties.
402
403(defun abbrev-table-get (table prop)
404 "Get the PROP property of abbrev table TABLE."
405 (let ((sym (intern-soft "" table)))
406 (if sym (get sym prop))))
407
408(defun abbrev-table-put (table prop val)
409 "Set the PROP property of abbrev table TABLE to VAL."
410 (let ((sym (intern "" table)))
411 (set sym nil) ; Make sure it won't be confused for an abbrev.
412 (put sym prop val)))
413
79415279
SM
414(defalias 'abbrev-get 'get
415 "Get the property PROP of abbrev ABBREV
416
417\(fn ABBREV PROP)")
418
419(defalias 'abbrev-put 'put
420 "Set the property PROP of abbrev ABREV to value VAL.
421See `define-abbrev' for the effect of some special properties.
422
423\(fn ABBREV PROP VAL)")
e047f448 424
e047f448
SM
425;;; Code that used to be implemented in src/abbrev.c
426
427(defvar abbrev-table-name-list '(fundamental-mode-abbrev-table
428 global-abbrev-table)
429 "List of symbols whose values are abbrev tables.")
430
431(defun make-abbrev-table (&optional props)
432 "Create a new, empty abbrev table object.
da59e7b2 433PROPS is a list of properties."
e047f448
SM
434 ;; The value 59 is an arbitrary prime number.
435 (let ((table (make-vector 59 0)))
436 ;; Each abbrev-table has a `modiff' counter which can be used to detect
437 ;; when an abbreviation was added. An example of use would be to
438 ;; construct :regexp dynamically as the union of all abbrev names, so
439 ;; `modiff' can let us detect that an abbrev was added and hence :regexp
440 ;; needs to be refreshed.
441 ;; The presence of `modiff' entry is also used as a tag indicating this
442 ;; vector is really an abbrev-table.
443 (abbrev-table-put table :abbrev-table-modiff 0)
444 (while (consp props)
445 (abbrev-table-put table (pop props) (pop props)))
446 table))
447
448(defun abbrev-table-p (object)
449 (and (vectorp object)
450 (numberp (abbrev-table-get object :abbrev-table-modiff))))
451
947b6566
LL
452(defun abbrev-table-empty-p (object &optional ignore-system)
453 "Return nil if there are no abbrev symbols in OBJECT.
454If IGNORE-SYSTEM is non-nil, system definitions are ignored."
455 (unless (abbrev-table-p object)
456 (error "Non abbrev table object"))
457 (not (catch 'some
458 (mapatoms (lambda (abbrev)
459 (unless (or (zerop (length (symbol-name abbrev)))
460 (and ignore-system
461 (abbrev-get abbrev :system)))
462 (throw 'some t)))
463 object))))
464
e047f448
SM
465(defvar global-abbrev-table (make-abbrev-table)
466 "The abbrev table whose abbrevs affect all buffers.
467Each buffer may also have a local abbrev table.
468If it does, the local table overrides the global one
469for any particular abbrev defined in both.")
470
471(defvar abbrev-minor-mode-table-alist nil
472 "Alist of abbrev tables to use for minor modes.
473Each element looks like (VARIABLE . ABBREV-TABLE);
474ABBREV-TABLE is active whenever VARIABLE's value is non-nil.")
475
476(defvar fundamental-mode-abbrev-table
477 (let ((table (make-abbrev-table)))
478 ;; Set local-abbrev-table's default to be fundamental-mode-abbrev-table.
479 (setq-default local-abbrev-table table)
480 table)
481 "The abbrev table of mode-specific abbrevs for Fundamental Mode.")
482
483(defvar abbrevs-changed nil
484 "Set non-nil by defining or altering any word abbrevs.
485This causes `save-some-buffers' to offer to save the abbrevs.")
486
487(defcustom abbrev-all-caps nil
488 "Non-nil means expand multi-word abbrevs all caps if abbrev was so."
489 :type 'boolean
490 :group 'abbrev-mode)
491
492(defvar abbrev-start-location nil
493 "Buffer position for `expand-abbrev' to use as the start of the abbrev.
494When nil, use the word before point as the abbrev.
495Calling `expand-abbrev' sets this to nil.")
496
497(defvar abbrev-start-location-buffer nil
498 "Buffer that `abbrev-start-location' has been set for.
499Trying to expand an abbrev in any other buffer clears `abbrev-start-location'.")
500
501(defvar last-abbrev nil
502 "The abbrev-symbol of the last abbrev expanded. See `abbrev-symbol'.")
503
504(defvar last-abbrev-text nil
505 "The exact text of the last abbrev expanded.
29c48340 506It is nil if the abbrev has already been unexpanded.")
e047f448
SM
507
508(defvar last-abbrev-location 0
509 "The location of the start of the last abbrev expanded.")
510
511;; (defvar local-abbrev-table fundamental-mode-abbrev-table
512;; "Local (mode-specific) abbrev table of current buffer.")
513;; (make-variable-buffer-local 'local-abbrev-table)
514
515(defcustom pre-abbrev-expand-hook nil
516 "Function or functions to be called before abbrev expansion is done.
517This is the first thing that `expand-abbrev' does, and so this may change
518the current abbrev table before abbrev lookup happens."
519 :type 'hook
520 :group 'abbrev-mode)
521(make-obsolete-variable 'pre-abbrev-expand-hook 'abbrev-expand-functions "23.1")
522
523(defun clear-abbrev-table (table)
524 "Undefine all abbrevs in abbrev table TABLE, leaving it empty."
525 (setq abbrevs-changed t)
0b281d03
SM
526 (let* ((sym (intern-soft "" table)))
527 (dotimes (i (length table))
528 (aset table i 0))
529 ;; Preserve the table's properties.
530 (assert sym)
938a9a9e
SM
531 (let ((newsym (intern "" table)))
532 (set newsym nil) ; Make sure it won't be confused for an abbrev.
533 (setplist newsym (symbol-plist sym)))
0b281d03 534 (abbrev-table-put table :abbrev-table-modiff
f13f18d9 535 (1+ (abbrev-table-get table :abbrev-table-modiff))))
ba9db1c5 536 ;; For backward compatibility, always return nil.
f13f18d9 537 nil)
e047f448
SM
538
539(defun define-abbrev (table name expansion &optional hook &rest props)
540 "Define an abbrev in TABLE named NAME, to expand to EXPANSION and call HOOK.
541NAME must be a string, and should be lower-case.
542EXPANSION should usually be a string.
543To undefine an abbrev, define it with EXPANSION = nil.
544If HOOK is non-nil, it should be a function of no arguments;
545it is called after EXPANSION is inserted.
434c0be6
CY
546If EXPANSION is not a string (and not nil), the abbrev is a
547 special one, which does not expand in the usual way but only
548 runs HOOK.
e047f448
SM
549
550PROPS is a property list. The following properties are special:
da59e7b2
SM
551- `:count': the value for the abbrev's usage-count, which is incremented each
552 time the abbrev is used (the default is zero).
79415279 553- `:system': if non-nil, says that this is a \"system\" abbreviation
e047f448 554 which should not be saved in the user's abbreviation file.
79415279 555 Unless `:system' is `force', a system abbreviation will not
e047f448
SM
556 overwrite a non-system abbreviation of the same name.
557- `:case-fixed': non-nil means that abbreviations are looked up without
558 case-folding, and the expansion is not capitalized/upcased.
64f00939 559- `:enable-function': a function of no argument which returns non-nil if the
e047f448
SM
560 abbrev should be used for a particular call of `expand-abbrev'.
561
562An obsolete but still supported calling form is:
563
79415279 564\(define-abbrev TABLE NAME EXPANSION &optional HOOK COUNT SYSTEM)."
e047f448
SM
565 (when (and (consp props) (or (null (car props)) (numberp (car props))))
566 ;; Old-style calling convention.
79415279
SM
567 (setq props (list* :count (car props)
568 (if (cadr props) (list :system (cadr props))))))
569 (unless (plist-get props :count)
570 (setq props (plist-put props :count 0)))
571 (let ((system-flag (plist-get props :system))
e047f448
SM
572 (sym (intern name table)))
573 ;; Don't override a prior user-defined abbrev with a system abbrev,
574 ;; unless system-flag is `force'.
575 (unless (and (not (memq system-flag '(nil force)))
576 (boundp sym) (symbol-value sym)
79415279 577 (not (abbrev-get sym :system)))
e047f448
SM
578 (unless (or system-flag
579 (and (boundp sym) (fboundp sym)
580 ;; load-file-name
581 (equal (symbol-value sym) expansion)
582 (equal (symbol-function sym) hook)))
583 (setq abbrevs-changed t))
584 (set sym expansion)
585 (fset sym hook)
79415279
SM
586 (setplist sym
587 ;; Don't store the `force' value of `system-flag' into
588 ;; the :system property.
589 (if (eq 'force system-flag) (plist-put props :system t) props))
e047f448
SM
590 (abbrev-table-put table :abbrev-table-modiff
591 (1+ (abbrev-table-get table :abbrev-table-modiff))))
592 name))
593
594(defun abbrev--check-chars (abbrev global)
595 "Check if the characters in ABBREV have word syntax in either the
596current (if global is nil) or standard syntax table."
597 (with-syntax-table
598 (cond ((null global) (standard-syntax-table))
599 ;; ((syntax-table-p global) global)
600 (t (syntax-table)))
601 (when (string-match "\\W" abbrev)
602 (let ((badchars ())
603 (pos 0))
604 (while (string-match "\\W" abbrev pos)
605 (pushnew (aref abbrev (match-beginning 0)) badchars)
606 (setq pos (1+ pos)))
607 (error "Some abbrev characters (%s) are not word constituents %s"
608 (apply 'string (nreverse badchars))
609 (if global "in the standard syntax" "in this mode"))))))
610
611(defun define-global-abbrev (abbrev expansion)
612 "Define ABBREV as a global abbreviation for EXPANSION.
613The characters in ABBREV must all be word constituents in the standard
614syntax table."
615 (interactive "sDefine global abbrev: \nsExpansion for %s: ")
616 (abbrev--check-chars abbrev 'global)
617 (define-abbrev global-abbrev-table (downcase abbrev) expansion))
618
619(defun define-mode-abbrev (abbrev expansion)
620 "Define ABBREV as a mode-specific abbreviation for EXPANSION.
621The characters in ABBREV must all be word-constituents in the current mode."
622 (interactive "sDefine mode abbrev: \nsExpansion for %s: ")
623 (unless local-abbrev-table
624 (error "Major mode has no abbrev table"))
625 (abbrev--check-chars abbrev nil)
626 (define-abbrev local-abbrev-table (downcase abbrev) expansion))
627
628(defun abbrev--active-tables (&optional tables)
629 "Return the list of abbrev tables currently active.
630TABLES if non-nil overrides the usual rules. It can hold
631either a single abbrev table or a list of abbrev tables."
632 ;; We could just remove the `tables' arg and let callers use
633 ;; (or table (abbrev--active-tables)) but then they'd have to be careful
634 ;; to treat the distinction between a single table and a list of tables.
635 (cond
636 ((consp tables) tables)
637 ((vectorp tables) (list tables))
638 (t
639 (let ((tables (if (listp local-abbrev-table)
640 (append local-abbrev-table
641 (list global-abbrev-table))
642 (list local-abbrev-table global-abbrev-table))))
643 ;; Add the minor-mode abbrev tables.
644 (dolist (x abbrev-minor-mode-table-alist)
645 (when (and (symbolp (car x)) (boundp (car x)) (symbol-value (car x)))
646 (setq tables
647 (if (listp (cdr x))
648 (append (cdr x) tables) (cons (cdr x) tables)))))
649 tables))))
f57a9512 650
e047f448
SM
651
652(defun abbrev-symbol (abbrev &optional table)
653 "Return the symbol representing abbrev named ABBREV.
654This symbol's name is ABBREV, but it is not the canonical symbol of that name;
655it is interned in an abbrev-table rather than the normal obarray.
656The value is nil if that abbrev is not defined.
657Optional second arg TABLE is abbrev table to look it up in.
658The default is to try buffer's mode-specific abbrev table, then global table."
659 (let ((tables (abbrev--active-tables table))
660 sym)
661 (while (and tables (not (symbol-value sym)))
2b86bfb1
SM
662 (let* ((table (pop tables))
663 (case-fold (not (abbrev-table-get table :case-fixed))))
e047f448
SM
664 (setq tables (append (abbrev-table-get table :parents) tables))
665 ;; In case the table doesn't set :case-fixed but some of the
666 ;; abbrevs do, we have to be careful.
667 (setq sym
668 ;; First try without case-folding.
669 (or (intern-soft abbrev table)
670 (when case-fold
671 ;; We didn't find any abbrev, try case-folding.
672 (let ((sym (intern-soft (downcase abbrev) table)))
673 ;; Only use it if it doesn't require :case-fixed.
674 (and sym (not (abbrev-get sym :case-fixed))
675 sym)))))))
676 (if (symbol-value sym)
677 sym)))
f57a9512 678
e047f448
SM
679
680(defun abbrev-expansion (abbrev &optional table)
681 "Return the string that ABBREV expands into in the current buffer.
682Optionally specify an abbrev table as second arg;
683then ABBREV is looked up in that table only."
684 (symbol-value (abbrev-symbol abbrev table)))
685
686
687(defun abbrev--before-point ()
688 "Try and find an abbrev before point. Return it if found, nil otherwise."
689 (unless (eq abbrev-start-location-buffer (current-buffer))
690 (setq abbrev-start-location nil))
691
692 (let ((tables (abbrev--active-tables))
693 (pos (point))
694 start end name res)
695
696 (if abbrev-start-location
697 (progn
698 (setq start abbrev-start-location)
699 (setq abbrev-start-location nil)
700 ;; Remove the hyphen inserted by `abbrev-prefix-mark'.
701 (if (and (< start (point-max))
702 (eq (char-after start) ?-))
703 (delete-region start (1+ start)))
704 (skip-syntax-backward " ")
705 (setq end (point))
2b86bfb1
SM
706 (when (> end start)
707 (setq name (buffer-substring start end))
708 (goto-char pos) ; Restore point.
709 (list (abbrev-symbol name tables) name start end)))
f57a9512 710
e047f448
SM
711 (while (and tables (not (car res)))
712 (let* ((table (pop tables))
713 (enable-fun (abbrev-table-get table :enable-function)))
714 (setq tables (append (abbrev-table-get table :parents) tables))
715 (setq res
716 (and (or (not enable-fun) (funcall enable-fun))
fe4346f0
SM
717 (let ((re (abbrev-table-get table :regexp)))
718 (if (null re)
719 ;; We used to default `re' to "\\<\\(\\w+\\)\\W*"
720 ;; but when words-include-escapes is set, that
721 ;; is not right and fixing it is boring.
722 (let ((lim (point)))
723 (backward-word 1)
724 (setq start (point))
725 (forward-word 1)
726 (setq end (min (point) lim)))
727 (when (looking-back re (line-beginning-position))
728 (setq start (match-beginning 1))
729 (setq end (match-end 1)))))
79415279
SM
730 (setq name (buffer-substring start end))
731 (let ((abbrev (abbrev-symbol name table)))
732 (when abbrev
733 (setq enable-fun (abbrev-get abbrev :enable-function))
734 (and (or (not enable-fun) (funcall enable-fun))
735 ;; This will also look it up in parent tables.
736 ;; This is not on purpose, but it seems harmless.
737 (list abbrev name start end))))))
e047f448
SM
738 ;; Restore point.
739 (goto-char pos)))
740 res)))
741
a3709a8c
SM
742(defun abbrev-insert (abbrev &optional name wordstart wordend)
743 "Insert abbrev ABBREV at point.
744If non-nil, NAME is the name by which this abbrev was found.
745If non-nil, WORDSTART is the place where to insert the abbrev.
78e4a31a 746If WORDEND is non-nil, the abbrev replaces the previous text between
a3709a8c
SM
747WORDSTART and WORDEND.
748Return ABBREV if the expansion should be considered as having taken place."
749 (unless name (setq name (symbol-name abbrev)))
750 (unless wordstart (setq wordstart (point)))
751 (unless wordend (setq wordend wordstart))
752 ;; Increment use count.
753 (abbrev-put abbrev :count (1+ (abbrev-get abbrev :count)))
754 (let ((value abbrev))
755 ;; If this abbrev has an expansion, delete the abbrev
756 ;; and insert the expansion.
757 (when (stringp (symbol-value abbrev))
758 (goto-char wordstart)
759 ;; Insert at beginning so that markers at the end (e.g. point)
760 ;; are preserved.
761 (insert (symbol-value abbrev))
762 (delete-char (- wordend wordstart))
763 (let ((case-fold-search nil))
764 ;; If the abbrev's name is different from the buffer text (the
765 ;; only difference should be capitalization), then we may want
766 ;; to adjust the capitalization of the expansion.
767 (when (and (not (equal name (symbol-name abbrev)))
768 (string-match "[[:upper:]]" name))
769 (if (not (string-match "[[:lower:]]" name))
770 ;; Abbrev was all caps. If expansion is multiple words,
771 ;; normally capitalize each word.
772 (if (and (not abbrev-all-caps)
773 (save-excursion
774 (> (progn (backward-word 1) (point))
775 (progn (goto-char wordstart)
776 (forward-word 1) (point)))))
777 (upcase-initials-region wordstart (point))
778 (upcase-region wordstart (point)))
779 ;; Abbrev included some caps. Cap first initial of expansion.
780 (let ((end (point)))
781 ;; Find the initial.
782 (goto-char wordstart)
783 (skip-syntax-forward "^w" (1- end))
784 ;; Change just that.
785 (upcase-initials-region (point) (1+ (point)))
786 (goto-char end))))))
787 ;; Now point is at the end of the expansion and the beginning is
788 ;; in last-abbrev-location.
789 (when (symbol-function abbrev)
790 (let* ((hook (symbol-function abbrev))
791 (expanded
792 ;; If the abbrev has a hook function, run it.
793 (funcall hook)))
794 ;; In addition, if the hook function is a symbol with
795 ;; a non-nil `no-self-insert' property, let the value it
796 ;; returned specify whether we consider that an expansion took
797 ;; place. If it returns nil, no expansion has been done.
798 (if (and (symbolp hook)
799 (null expanded)
800 (get hook 'no-self-insert))
801 (setq value nil))))
802 value))
803
e047f448
SM
804(defvar abbrev-expand-functions nil
805 "Wrapper hook around `expand-abbrev'.
806The functions on this special hook are called with one argument:
807a function that performs the abbrev expansion. It should return
808the abbrev symbol if expansion took place.")
809
810(defun expand-abbrev ()
811 "Expand the abbrev before point, if there is an abbrev there.
812Effective when explicitly called even when `abbrev-mode' is nil.
813Returns the abbrev symbol, if expansion took place."
814 (interactive)
815 (run-hooks 'pre-abbrev-expand-hook)
a185548b 816 (with-wrapper-hook abbrev-expand-functions ()
e047f448
SM
817 (destructuring-bind (&optional sym name wordstart wordend)
818 (abbrev--before-point)
819 (when sym
856b2f11
SM
820 (let ((startpos (copy-marker (point) t))
821 (endmark (copy-marker wordend t)))
822 (unless (or ;; executing-kbd-macro
823 noninteractive
824 (window-minibuffer-p (selected-window)))
825 ;; Add an undo boundary, in case we are doing this for
826 ;; a self-inserting command which has avoided making one so far.
827 (undo-boundary))
828 ;; Now sym is the abbrev symbol.
829 (setq last-abbrev-text name)
830 (setq last-abbrev sym)
831 (setq last-abbrev-location wordstart)
832 ;; If this abbrev has an expansion, delete the abbrev
833 ;; and insert the expansion.
834 (prog1
835 (abbrev-insert sym name wordstart wordend)
836 ;; Yuck!! If expand-abbrev is called with point slightly
837 ;; further than the end of the abbrev, move point back to
838 ;; where it started.
839 (if (and (> startpos endmark)
840 (= (point) endmark)) ;Obey skeletons that move point.
841 (goto-char startpos))))))))
e047f448
SM
842
843(defun unexpand-abbrev ()
844 "Undo the expansion of the last abbrev that expanded.
845This differs from ordinary undo in that other editing done since then
846is not undone."
847 (interactive)
848 (save-excursion
849 (unless (or (< last-abbrev-location (point-min))
850 (> last-abbrev-location (point-max)))
851 (goto-char last-abbrev-location)
852 (when (stringp last-abbrev-text)
853 ;; This isn't correct if last-abbrev's hook was used
854 ;; to do the expansion.
855 (let ((val (symbol-value last-abbrev)))
856 (unless (stringp val)
f68cfe84 857 (error "Value of abbrev-symbol must be a string"))
e047f448
SM
858 ;; Don't inherit properties here; just copy from old contents.
859 (insert last-abbrev-text)
f68cfe84
SM
860 ;; Delete after inserting, to better preserve markers.
861 (delete-region (point) (+ (point) (length val)))
e047f448
SM
862 (setq last-abbrev-text nil))))))
863
864(defun abbrev--write (sym)
865 "Write the abbrev in a `read'able form.
866Only writes the non-system abbrevs.
867Presumes that `standard-output' points to `current-buffer'."
79415279 868 (unless (or (null (symbol-value sym)) (abbrev-get sym :system))
e047f448 869 (insert " (")
d548715c 870 (prin1 (symbol-name sym))
e047f448
SM
871 (insert " ")
872 (prin1 (symbol-value sym))
873 (insert " ")
874 (prin1 (symbol-function sym))
875 (insert " ")
79415279 876 (prin1 (abbrev-get sym :count))
e047f448
SM
877 (insert ")\n")))
878
879(defun abbrev--describe (sym)
880 (when (symbol-value sym)
881 (prin1 (symbol-name sym))
79415279 882 (if (null (abbrev-get sym :system))
e047f448
SM
883 (indent-to 15 1)
884 (insert " (sys)")
885 (indent-to 20 1))
79415279 886 (prin1 (abbrev-get sym :count))
e047f448
SM
887 (indent-to 20 1)
888 (prin1 (symbol-value sym))
889 (when (symbol-function sym)
890 (indent-to 45 1)
891 (prin1 (symbol-function sym)))
892 (terpri)))
893
894(defun insert-abbrev-table-description (name &optional readable)
895 "Insert before point a full description of abbrev table named NAME.
896NAME is a symbol whose value is an abbrev table.
897If optional 2nd arg READABLE is non-nil, a human-readable description
898is inserted. Otherwise the description is an expression,
899a call to `define-abbrev-table', which would
900define the abbrev table NAME exactly as it is currently defined.
901
902Abbrevs marked as \"system abbrevs\" are omitted."
903 (let ((table (symbol-value name))
904 (symbols ()))
905 (mapatoms (lambda (sym) (if (symbol-value sym) (push sym symbols))) table)
906 (setq symbols (sort symbols 'string-lessp))
907 (let ((standard-output (current-buffer)))
908 (if readable
909 (progn
910 (insert "(")
911 (prin1 name)
912 (insert ")\n\n")
913 (mapc 'abbrev--describe symbols)
914 (insert "\n\n"))
915 (insert "(define-abbrev-table '")
916 (prin1 name)
42da39c1
CY
917 (if (null symbols)
918 (insert " '())\n\n")
919 (insert "\n '(\n")
920 (mapc 'abbrev--write symbols)
921 (insert " ))\n\n")))
e047f448
SM
922 nil)))
923
e1ca6a5b 924(put 'define-abbrev-table 'doc-string-elt 3)
e047f448
SM
925(defun define-abbrev-table (tablename definitions
926 &optional docstring &rest props)
927 "Define TABLENAME (a symbol) as an abbrev table name.
928Define abbrevs in it according to DEFINITIONS, which is a list of elements
da59e7b2 929of the form (ABBREVNAME EXPANSION ...) that are passed to `define-abbrev'.
e047f448
SM
930PROPS is a property list to apply to the table.
931Properties with special meaning:
932- `:parents' contains a list of abbrev tables from which this table inherits
933 abbreviations.
934- `:case-fixed' non-nil means that abbreviations are looked up without
935 case-folding, and the expansion is not capitalized/upcased.
f57a9512 936- `:regexp' describes the form of abbrevs. It defaults to \\=\\<\\(\\w+\\)\\W* which
e047f448
SM
937 means that an abbrev can only be a single word. The submatch 1 is treated
938 as the potential name of an abbrev.
939- `:enable-function' can be set to a function of no argument which returns
d239a055
JB
940 non-nil if and only if the abbrevs in this table should be used for this
941 instance of `expand-abbrev'."
4eebd7fe
SM
942 ;; We used to manually add the docstring, but we also want to record this
943 ;; location as the definition of the variable (in load-history), so we may
944 ;; as well just use `defvar'.
945 (eval `(defvar ,tablename nil ,@(if (stringp docstring) (list docstring))))
e047f448
SM
946 (let ((table (if (boundp tablename) (symbol-value tablename))))
947 (unless table
9e2a4d4d 948 (setq table (make-abbrev-table))
e047f448 949 (set tablename table)
2b75be67
SM
950 (unless (memq tablename abbrev-table-name-list)
951 (push tablename abbrev-table-name-list)))
9e2a4d4d
SM
952 ;; We used to just pass them to `make-abbrev-table', but that fails
953 ;; if the table was pre-existing as is the case if it was created by
954 ;; loading the user's abbrev file.
955 (while (consp props)
956 (abbrev-table-put table (pop props) (pop props)))
e047f448
SM
957 (dolist (elt definitions)
958 (apply 'define-abbrev table elt))))
959
a3709a8c
SM
960(defun abbrev-table-menu (table &optional prompt sortfun)
961 "Return a menu that shows all abbrevs in TABLE.
962Selecting an entry runs `abbrev-insert'.
963PROMPT is the prompt to use for the keymap.
964SORTFUN is passed to `sort' to change the default ordering."
965 (unless sortfun (setq sortfun 'string-lessp))
966 (let ((entries ()))
967 (mapatoms (lambda (abbrev)
968 (when (symbol-value abbrev)
969 (let ((name (symbol-name abbrev)))
970 (push `(,(intern name) menu-item ,name
971 (lambda () (interactive)
972 (abbrev-insert ',abbrev)))
973 entries))))
974 table)
975 (nconc (make-sparse-keymap prompt)
976 (sort entries (lambda (x y)
977 (funcall sortfun (nth 2 x) (nth 2 y)))))))
978
c06d4c1f
RS
979(provide 'abbrev)
980
c0274f38 981;;; abbrev.el ends here