(Ffile_attributes) [WINDOWSNT]: Force inode be positive.
[bpt/emacs.git] / lisp / abbrev.el
CommitLineData
c0274f38 1;;; abbrev.el --- abbrev mode commands for Emacs
e2fbf49d 2
e91081eb 3;; Copyright (C) 1985, 1986, 1987, 1992, 2001, 2002, 2003, 2004,
409cc4a3 4;; 2005, 2006, 2007, 2008 Free Software Foundation, Inc.
e2fbf49d 5
6228c05b 6;; Maintainer: FSF
f5f727f8 7;; Keywords: abbrev convenience
e9571d2a 8
e2fbf49d
JB
9;; This file is part of GNU Emacs.
10
11;; GNU Emacs is free software; you can redistribute it and/or modify
12;; it under the terms of the GNU General Public License as published by
b4aa6026 13;; the Free Software Foundation; either version 3, or (at your option)
e2fbf49d
JB
14;; any later version.
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
b578f267 22;; along with GNU Emacs; see the file COPYING. If not, write to the
086add15
LK
23;; Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
24;; Boston, MA 02110-1301, USA.
e2fbf49d 25
e41b2db1
ER
26;;; Commentary:
27
28;; This facility is documented in the Emacs Manual.
29
e047f448
SM
30;; Todo:
31
32;; - Make abbrev-file-name obey user-emacs-directory.
33;; - Cleanup name space.
34
e5167999 35;;; Code:
e2fbf49d 36
e047f448
SM
37(eval-when-compile (require 'cl))
38
39(defgroup abbrev-mode nil
40 "Word abbreviations mode."
41 :link '(custom-manual "(emacs)Abbrevs")
42 :group 'abbrev)
43
7cfedc97 44(defcustom only-global-abbrevs nil
e7fdaf63 45 "Non-nil means user plans to use global abbrevs only.
1d1e35a0
RS
46This makes the commands that normally define mode-specific abbrevs
47define global abbrevs instead."
48 :type 'boolean
f5f727f8
DN
49 :group 'abbrev-mode
50 :group 'convenience)
e2fbf49d 51
68d9550f 52(defun abbrev-mode (&optional arg)
38002bff 53 "Toggle Abbrev mode in the current buffer.
4837b516
GM
54With optional argument ARG, turn abbrev mode on if ARG is
55positive, otherwise turn it off. In Abbrev mode, inserting an
56abbreviation causes it to expand and be replaced by its expansion."
e2fbf49d
JB
57 (interactive "P")
58 (setq abbrev-mode
59 (if (null arg) (not abbrev-mode)
60 (> (prefix-numeric-value arg) 0)))
e47df26a 61 (force-mode-line-update))
1d1e35a0
RS
62
63(defcustom abbrev-mode nil
38002bff 64 "Enable or disable Abbrev mode.
5cf1c7ac
RS
65Non-nil means automatically expand abbrevs as they are inserted.
66
38002bff 67Setting this variable with `setq' changes it for the current buffer.
5cf1c7ac 68Changing it with \\[customize] sets the default value.
38002bff
RS
69Interactively, use the command `abbrev-mode'
70to enable or disable Abbrev mode in the current buffer."
1d1e35a0
RS
71 :type 'boolean
72 :group 'abbrev-mode)
65f21967 73;;;###autoload(put 'abbrev-mode 'safe-local-variable 'booleanp)
1d1e35a0 74
e2fbf49d 75\f
e7fdaf63
JPW
76(defvar edit-abbrevs-map
77 (let ((map (make-sparse-keymap)))
78 (define-key map "\C-x\C-s" 'edit-abbrevs-redefine)
79 (define-key map "\C-c\C-c" 'edit-abbrevs-redefine)
80 map)
38002bff 81 "Keymap used in `edit-abbrevs'.")
e2fbf49d
JB
82
83(defun kill-all-abbrevs ()
84 "Undefine all defined abbrevs."
85 (interactive)
0b281d03
SM
86 (dolist (tablesym abbrev-table-name-list)
87 (clear-abbrev-table (symbol-value tablesym))))
e2fbf49d 88
ad9d51b2
RS
89(defun copy-abbrev-table (table)
90 "Make a new abbrev-table with the same abbrevs as TABLE."
91 (let ((new-table (make-abbrev-table)))
92 (mapatoms
93 (lambda (symbol)
94 (define-abbrev new-table
95 (symbol-name symbol)
96 (symbol-value symbol)
97 (symbol-function symbol)))
98 table)
99 new-table))
100
e2fbf49d
JB
101(defun insert-abbrevs ()
102 "Insert after point a description of all defined abbrevs.
103Mark is set after the inserted text."
104 (interactive)
105 (push-mark
106 (save-excursion
0b281d03
SM
107 (dolist (tablesym abbrev-table-name-list)
108 (insert-abbrev-table-description tablesym t))
71baa28f 109 (point))))
e2fbf49d 110
c88a9944
GM
111(defun list-abbrevs (&optional local)
112 "Display a list of defined abbrevs.
113If LOCAL is non-nil, interactively when invoked with a
114prefix arg, display only local, i.e. mode-specific, abbrevs.
115Otherwise display all abbrevs."
116 (interactive "P")
117 (display-buffer (prepare-abbrev-list-buffer local)))
118
119(defun abbrev-table-name (table)
120 "Value is the name of abbrev table TABLE."
121 (let ((tables abbrev-table-name-list)
122 found)
123 (while (and (not found) tables)
124 (when (eq (symbol-value (car tables)) table)
125 (setq found (car tables)))
126 (setq tables (cdr tables)))
127 found))
7cfedc97 128
c88a9944 129(defun prepare-abbrev-list-buffer (&optional local)
0b281d03
SM
130 (with-current-buffer (get-buffer-create "*Abbrevs*")
131 (erase-buffer)
132 (if local
133 (insert-abbrev-table-description
134 (abbrev-table-name local-abbrev-table) t)
135 (dolist (table abbrev-table-name-list)
136 (insert-abbrev-table-description table t)))
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)
165 (switch-to-buffer (prepare-abbrev-list-buffer)))
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
68063965
LT
223(defun write-abbrev-file (&optional file)
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
LT
228The argument FILE is the file name to write. If omitted or nil, the file
229specified in `abbrev-file-name' is used."
e2fbf49d
JB
230 (interactive
231 (list
232 (read-file-name "Write abbrev file: "
233 (file-name-directory (expand-file-name abbrev-file-name))
234 abbrev-file-name)))
e2fbf49d
JB
235 (or (and file (> (length file) 0))
236 (setq file abbrev-file-name))
a166f623
DL
237 (let ((coding-system-for-write 'emacs-mule))
238 (with-temp-file file
239 (insert ";;-*-coding: emacs-mule;-*-\n")
71baa28f
EZ
240 (dolist (table
241 ;; We sort the table in order to ease the automatic
242 ;; merging of different versions of the user's abbrevs
243 ;; file. This is useful, for example, for when the
244 ;; user keeps their home directory in a revision
245 ;; control system, and is therefore keeping multiple
246 ;; slightly-differing copies loosely synchronized.
247 (sort (copy-sequence abbrev-table-name-list)
248 (lambda (s1 s2)
249 (string< (symbol-name s1)
250 (symbol-name s2)))))
a166f623 251 (insert-abbrev-table-description table nil)))))
e2fbf49d
JB
252\f
253(defun add-mode-abbrev (arg)
254 "Define mode-specific abbrev for last word(s) before point.
255Argument is how many words before point form the expansion;
256or zero means the region is the expansion.
257A negative argument means to undefine the specified abbrev.
258Reads the abbreviation in the minibuffer.
259
260Don't use this function in a Lisp program; use `define-abbrev' instead."
261 (interactive "p")
262 (add-abbrev
263 (if only-global-abbrevs
71296446 264 global-abbrev-table
e2fbf49d
JB
265 (or local-abbrev-table
266 (error "No per-mode abbrev table")))
267 "Mode" arg))
268
269(defun add-global-abbrev (arg)
270 "Define global (all modes) abbrev for last word(s) before point.
271The prefix argument specifies the number of words before point that form the
272expansion; or zero means the region is the expansion.
273A negative argument means to undefine the specified abbrev.
274This command uses the minibuffer to read the abbreviation.
275
276Don't use this function in a Lisp program; use `define-abbrev' instead."
277 (interactive "p")
278 (add-abbrev global-abbrev-table "Global" arg))
279
280(defun add-abbrev (table type arg)
281 (let ((exp (and (>= arg 0)
34f3cd03 282 (buffer-substring-no-properties
e2fbf49d
JB
283 (point)
284 (if (= arg 0) (mark)
285 (save-excursion (forward-word (- arg)) (point))))))
286 name)
287 (setq name
288 (read-string (format (if exp "%s abbrev for \"%s\": "
289 "Undefine %s abbrev: ")
290 type exp)))
a0f88464 291 (set-text-properties 0 (length name) nil name)
e2fbf49d
JB
292 (if (or (null exp)
293 (not (abbrev-expansion name table))
294 (y-or-n-p (format "%s expands to \"%s\"; redefine? "
295 name (abbrev-expansion name table))))
296 (define-abbrev table (downcase name) exp))))
7cfedc97 297
e66b273f 298(defun inverse-add-mode-abbrev (n)
e2fbf49d
JB
299 "Define last word before point as a mode-specific abbrev.
300With prefix argument N, defines the Nth word before point.
301This command uses the minibuffer to read the expansion.
302Expands the abbreviation after defining it."
303 (interactive "p")
304 (inverse-add-abbrev
305 (if only-global-abbrevs
7cfedc97 306 global-abbrev-table
e2fbf49d
JB
307 (or local-abbrev-table
308 (error "No per-mode abbrev table")))
e66b273f 309 "Mode" n))
e2fbf49d 310
e66b273f 311(defun inverse-add-global-abbrev (n)
e2fbf49d
JB
312 "Define last word before point as a global (mode-independent) abbrev.
313With prefix argument N, defines the Nth word before point.
314This command uses the minibuffer to read the expansion.
315Expands the abbreviation after defining it."
316 (interactive "p")
e66b273f 317 (inverse-add-abbrev global-abbrev-table "Global" n))
e2fbf49d
JB
318
319(defun inverse-add-abbrev (table type arg)
46684068 320 (let (name exp start end)
e2fbf49d 321 (save-excursion
46684068
GM
322 (forward-word (1+ (- arg)))
323 (setq end (point))
324 (backward-word 1)
325 (setq start (point)
326 name (buffer-substring-no-properties start end)))
327
328 (setq exp (read-string (format "%s expansion for \"%s\": " type name)
329 nil nil nil t))
330 (when (or (not (abbrev-expansion name table))
331 (y-or-n-p (format "%s expands to \"%s\"; redefine? "
332 name (abbrev-expansion name table))))
333 (define-abbrev table (downcase name) exp)
334 (save-excursion
335 (goto-char end)
336 (expand-abbrev)))))
e2fbf49d
JB
337
338(defun abbrev-prefix-mark (&optional arg)
339 "Mark current point as the beginning of an abbrev.
340Abbrev to be expanded starts here rather than at beginning of word.
341This way, you can expand an abbrev with a prefix: insert the prefix,
68063965
LT
342use this command, then insert the abbrev. This command inserts a
343temporary hyphen after the prefix \(until the intended abbrev
344expansion occurs).
345If the prefix is itself an abbrev, this command expands it, unless
346ARG is non-nil. Interactively, ARG is the prefix argument."
e2fbf49d
JB
347 (interactive "P")
348 (or arg (expand-abbrev))
349 (setq abbrev-start-location (point-marker)
350 abbrev-start-location-buffer (current-buffer))
351 (insert "-"))
352
353(defun expand-region-abbrevs (start end &optional noquery)
354 "For abbrev occurrence in the region, offer to expand it.
e66b273f
JB
355The user is asked to type `y' or `n' for each occurrence.
356A prefix argument means don't query; expand all abbrevs."
e2fbf49d
JB
357 (interactive "r\nP")
358 (save-excursion
359 (goto-char start)
360 (let ((lim (- (point-max) end))
361 pnt string)
362 (while (and (not (eobp))
363 (progn (forward-word 1)
364 (<= (setq pnt (point)) (- (point-max) lim))))
365 (if (abbrev-expansion
366 (setq string
34f3cd03 367 (buffer-substring-no-properties
e2fbf49d
JB
368 (save-excursion (forward-word -1) (point))
369 pnt)))
370 (if (or noquery (y-or-n-p (format "Expand `%s'? " string)))
371 (expand-abbrev)))))))
c0274f38 372
e047f448
SM
373;;; Abbrev properties.
374
375(defun abbrev-table-get (table prop)
376 "Get the PROP property of abbrev table TABLE."
377 (let ((sym (intern-soft "" table)))
378 (if sym (get sym prop))))
379
380(defun abbrev-table-put (table prop val)
381 "Set the PROP property of abbrev table TABLE to VAL."
382 (let ((sym (intern "" table)))
383 (set sym nil) ; Make sure it won't be confused for an abbrev.
384 (put sym prop val)))
385
79415279
SM
386(defalias 'abbrev-get 'get
387 "Get the property PROP of abbrev ABBREV
388
389\(fn ABBREV PROP)")
390
391(defalias 'abbrev-put 'put
392 "Set the property PROP of abbrev ABREV to value VAL.
393See `define-abbrev' for the effect of some special properties.
394
395\(fn ABBREV PROP VAL)")
e047f448
SM
396
397(defmacro abbrev-with-wrapper-hook (var &rest body)
398 "Run BODY wrapped with the VAR hook.
399VAR is a special hook: its functions are called with one argument which
400is the \"original\" code (the BODY), so the hook function can wrap the
401original function, can call it several times, or even not call it at all.
402VAR is normally a symbol (a variable) in which case it is treated like a hook,
403with a buffer-local and a global part. But it can also be an arbitrary expression.
404This is similar to an `around' advice."
405 (declare (indent 1) (debug t))
406 ;; We need those two gensyms because CL's lexical scoping is not available
407 ;; for function arguments :-(
408 (let ((funs (make-symbol "funs"))
409 (global (make-symbol "global")))
410 ;; Since the hook is a wrapper, the loop has to be done via
411 ;; recursion: a given hook function will call its parameter in order to
412 ;; continue looping.
413 `(labels ((runrestofhook (,funs ,global)
414 ;; `funs' holds the functions left on the hook and `global'
415 ;; holds the functions left on the global part of the hook
416 ;; (in case the hook is local).
417 (lexical-let ((funs ,funs)
418 (global ,global))
419 (if (consp funs)
420 (if (eq t (car funs))
421 (runrestofhook (append global (cdr funs)) nil)
422 (funcall (car funs)
423 (lambda () (runrestofhook (cdr funs) global))))
424 ;; Once there are no more functions on the hook, run
425 ;; the original body.
426 ,@body))))
427 (runrestofhook ,var
428 ;; The global part of the hook, if any.
429 ,(if (symbolp var)
430 `(if (local-variable-p ',var)
431 (default-value ',var)))))))
f57a9512 432
e047f448
SM
433
434;;; Code that used to be implemented in src/abbrev.c
435
436(defvar abbrev-table-name-list '(fundamental-mode-abbrev-table
437 global-abbrev-table)
438 "List of symbols whose values are abbrev tables.")
439
440(defun make-abbrev-table (&optional props)
441 "Create a new, empty abbrev table object.
442PROPS is a "
443 ;; The value 59 is an arbitrary prime number.
444 (let ((table (make-vector 59 0)))
445 ;; Each abbrev-table has a `modiff' counter which can be used to detect
446 ;; when an abbreviation was added. An example of use would be to
447 ;; construct :regexp dynamically as the union of all abbrev names, so
448 ;; `modiff' can let us detect that an abbrev was added and hence :regexp
449 ;; needs to be refreshed.
450 ;; The presence of `modiff' entry is also used as a tag indicating this
451 ;; vector is really an abbrev-table.
452 (abbrev-table-put table :abbrev-table-modiff 0)
453 (while (consp props)
454 (abbrev-table-put table (pop props) (pop props)))
455 table))
456
457(defun abbrev-table-p (object)
458 (and (vectorp object)
459 (numberp (abbrev-table-get object :abbrev-table-modiff))))
460
461(defvar global-abbrev-table (make-abbrev-table)
462 "The abbrev table whose abbrevs affect all buffers.
463Each buffer may also have a local abbrev table.
464If it does, the local table overrides the global one
465for any particular abbrev defined in both.")
466
467(defvar abbrev-minor-mode-table-alist nil
468 "Alist of abbrev tables to use for minor modes.
469Each element looks like (VARIABLE . ABBREV-TABLE);
470ABBREV-TABLE is active whenever VARIABLE's value is non-nil.")
471
472(defvar fundamental-mode-abbrev-table
473 (let ((table (make-abbrev-table)))
474 ;; Set local-abbrev-table's default to be fundamental-mode-abbrev-table.
475 (setq-default local-abbrev-table table)
476 table)
477 "The abbrev table of mode-specific abbrevs for Fundamental Mode.")
478
479(defvar abbrevs-changed nil
480 "Set non-nil by defining or altering any word abbrevs.
481This causes `save-some-buffers' to offer to save the abbrevs.")
482
483(defcustom abbrev-all-caps nil
484 "Non-nil means expand multi-word abbrevs all caps if abbrev was so."
485 :type 'boolean
486 :group 'abbrev-mode)
487
488(defvar abbrev-start-location nil
489 "Buffer position for `expand-abbrev' to use as the start of the abbrev.
490When nil, use the word before point as the abbrev.
491Calling `expand-abbrev' sets this to nil.")
492
493(defvar abbrev-start-location-buffer nil
494 "Buffer that `abbrev-start-location' has been set for.
495Trying to expand an abbrev in any other buffer clears `abbrev-start-location'.")
496
497(defvar last-abbrev nil
498 "The abbrev-symbol of the last abbrev expanded. See `abbrev-symbol'.")
499
500(defvar last-abbrev-text nil
501 "The exact text of the last abbrev expanded.
502nil if the abbrev has already been unexpanded.")
503
504(defvar last-abbrev-location 0
505 "The location of the start of the last abbrev expanded.")
506
507;; (defvar local-abbrev-table fundamental-mode-abbrev-table
508;; "Local (mode-specific) abbrev table of current buffer.")
509;; (make-variable-buffer-local 'local-abbrev-table)
510
511(defcustom pre-abbrev-expand-hook nil
512 "Function or functions to be called before abbrev expansion is done.
513This is the first thing that `expand-abbrev' does, and so this may change
514the current abbrev table before abbrev lookup happens."
515 :type 'hook
516 :group 'abbrev-mode)
517(make-obsolete-variable 'pre-abbrev-expand-hook 'abbrev-expand-functions "23.1")
518
519(defun clear-abbrev-table (table)
520 "Undefine all abbrevs in abbrev table TABLE, leaving it empty."
521 (setq abbrevs-changed t)
0b281d03
SM
522 (let* ((sym (intern-soft "" table)))
523 (dotimes (i (length table))
524 (aset table i 0))
525 ;; Preserve the table's properties.
526 (assert sym)
938a9a9e
SM
527 (let ((newsym (intern "" table)))
528 (set newsym nil) ; Make sure it won't be confused for an abbrev.
529 (setplist newsym (symbol-plist sym)))
0b281d03
SM
530 (abbrev-table-put table :abbrev-table-modiff
531 (1+ (abbrev-table-get table :abbrev-table-modiff)))))
e047f448
SM
532
533(defun define-abbrev (table name expansion &optional hook &rest props)
534 "Define an abbrev in TABLE named NAME, to expand to EXPANSION and call HOOK.
535NAME must be a string, and should be lower-case.
536EXPANSION should usually be a string.
537To undefine an abbrev, define it with EXPANSION = nil.
538If HOOK is non-nil, it should be a function of no arguments;
539it is called after EXPANSION is inserted.
540If EXPANSION is not a string, the abbrev is a special one,
541 which does not expand in the usual way but only runs HOOK.
542
543PROPS is a property list. The following properties are special:
79415279 544- `:count': the value for the abbrev's usage-count, which is incremented each time
e047f448 545 the abbrev is used (the default is zero).
79415279 546- `:system': if non-nil, says that this is a \"system\" abbreviation
e047f448 547 which should not be saved in the user's abbreviation file.
79415279 548 Unless `:system' is `force', a system abbreviation will not
e047f448
SM
549 overwrite a non-system abbreviation of the same name.
550- `:case-fixed': non-nil means that abbreviations are looked up without
551 case-folding, and the expansion is not capitalized/upcased.
552- `:enable-function': a function of no argument which returns non-nil iff the
553 abbrev should be used for a particular call of `expand-abbrev'.
554
555An obsolete but still supported calling form is:
556
79415279 557\(define-abbrev TABLE NAME EXPANSION &optional HOOK COUNT SYSTEM)."
e047f448
SM
558 (when (and (consp props) (or (null (car props)) (numberp (car props))))
559 ;; Old-style calling convention.
79415279
SM
560 (setq props (list* :count (car props)
561 (if (cadr props) (list :system (cadr props))))))
562 (unless (plist-get props :count)
563 (setq props (plist-put props :count 0)))
564 (let ((system-flag (plist-get props :system))
e047f448
SM
565 (sym (intern name table)))
566 ;; Don't override a prior user-defined abbrev with a system abbrev,
567 ;; unless system-flag is `force'.
568 (unless (and (not (memq system-flag '(nil force)))
569 (boundp sym) (symbol-value sym)
79415279 570 (not (abbrev-get sym :system)))
e047f448
SM
571 (unless (or system-flag
572 (and (boundp sym) (fboundp sym)
573 ;; load-file-name
574 (equal (symbol-value sym) expansion)
575 (equal (symbol-function sym) hook)))
576 (setq abbrevs-changed t))
577 (set sym expansion)
578 (fset sym hook)
79415279
SM
579 (setplist sym
580 ;; Don't store the `force' value of `system-flag' into
581 ;; the :system property.
582 (if (eq 'force system-flag) (plist-put props :system t) props))
e047f448
SM
583 (abbrev-table-put table :abbrev-table-modiff
584 (1+ (abbrev-table-get table :abbrev-table-modiff))))
585 name))
586
587(defun abbrev--check-chars (abbrev global)
588 "Check if the characters in ABBREV have word syntax in either the
589current (if global is nil) or standard syntax table."
590 (with-syntax-table
591 (cond ((null global) (standard-syntax-table))
592 ;; ((syntax-table-p global) global)
593 (t (syntax-table)))
594 (when (string-match "\\W" abbrev)
595 (let ((badchars ())
596 (pos 0))
597 (while (string-match "\\W" abbrev pos)
598 (pushnew (aref abbrev (match-beginning 0)) badchars)
599 (setq pos (1+ pos)))
600 (error "Some abbrev characters (%s) are not word constituents %s"
601 (apply 'string (nreverse badchars))
602 (if global "in the standard syntax" "in this mode"))))))
603
604(defun define-global-abbrev (abbrev expansion)
605 "Define ABBREV as a global abbreviation for EXPANSION.
606The characters in ABBREV must all be word constituents in the standard
607syntax table."
608 (interactive "sDefine global abbrev: \nsExpansion for %s: ")
609 (abbrev--check-chars abbrev 'global)
610 (define-abbrev global-abbrev-table (downcase abbrev) expansion))
611
612(defun define-mode-abbrev (abbrev expansion)
613 "Define ABBREV as a mode-specific abbreviation for EXPANSION.
614The characters in ABBREV must all be word-constituents in the current mode."
615 (interactive "sDefine mode abbrev: \nsExpansion for %s: ")
616 (unless local-abbrev-table
617 (error "Major mode has no abbrev table"))
618 (abbrev--check-chars abbrev nil)
619 (define-abbrev local-abbrev-table (downcase abbrev) expansion))
620
621(defun abbrev--active-tables (&optional tables)
622 "Return the list of abbrev tables currently active.
623TABLES if non-nil overrides the usual rules. It can hold
624either a single abbrev table or a list of abbrev tables."
625 ;; We could just remove the `tables' arg and let callers use
626 ;; (or table (abbrev--active-tables)) but then they'd have to be careful
627 ;; to treat the distinction between a single table and a list of tables.
628 (cond
629 ((consp tables) tables)
630 ((vectorp tables) (list tables))
631 (t
632 (let ((tables (if (listp local-abbrev-table)
633 (append local-abbrev-table
634 (list global-abbrev-table))
635 (list local-abbrev-table global-abbrev-table))))
636 ;; Add the minor-mode abbrev tables.
637 (dolist (x abbrev-minor-mode-table-alist)
638 (when (and (symbolp (car x)) (boundp (car x)) (symbol-value (car x)))
639 (setq tables
640 (if (listp (cdr x))
641 (append (cdr x) tables) (cons (cdr x) tables)))))
642 tables))))
f57a9512 643
e047f448
SM
644
645(defun abbrev-symbol (abbrev &optional table)
646 "Return the symbol representing abbrev named ABBREV.
647This symbol's name is ABBREV, but it is not the canonical symbol of that name;
648it is interned in an abbrev-table rather than the normal obarray.
649The value is nil if that abbrev is not defined.
650Optional second arg TABLE is abbrev table to look it up in.
651The default is to try buffer's mode-specific abbrev table, then global table."
652 (let ((tables (abbrev--active-tables table))
653 sym)
654 (while (and tables (not (symbol-value sym)))
2b86bfb1
SM
655 (let* ((table (pop tables))
656 (case-fold (not (abbrev-table-get table :case-fixed))))
e047f448
SM
657 (setq tables (append (abbrev-table-get table :parents) tables))
658 ;; In case the table doesn't set :case-fixed but some of the
659 ;; abbrevs do, we have to be careful.
660 (setq sym
661 ;; First try without case-folding.
662 (or (intern-soft abbrev table)
663 (when case-fold
664 ;; We didn't find any abbrev, try case-folding.
665 (let ((sym (intern-soft (downcase abbrev) table)))
666 ;; Only use it if it doesn't require :case-fixed.
667 (and sym (not (abbrev-get sym :case-fixed))
668 sym)))))))
669 (if (symbol-value sym)
670 sym)))
f57a9512 671
e047f448
SM
672
673(defun abbrev-expansion (abbrev &optional table)
674 "Return the string that ABBREV expands into in the current buffer.
675Optionally specify an abbrev table as second arg;
676then ABBREV is looked up in that table only."
677 (symbol-value (abbrev-symbol abbrev table)))
678
679
680(defun abbrev--before-point ()
681 "Try and find an abbrev before point. Return it if found, nil otherwise."
682 (unless (eq abbrev-start-location-buffer (current-buffer))
683 (setq abbrev-start-location nil))
684
685 (let ((tables (abbrev--active-tables))
686 (pos (point))
687 start end name res)
688
689 (if abbrev-start-location
690 (progn
691 (setq start abbrev-start-location)
692 (setq abbrev-start-location nil)
693 ;; Remove the hyphen inserted by `abbrev-prefix-mark'.
694 (if (and (< start (point-max))
695 (eq (char-after start) ?-))
696 (delete-region start (1+ start)))
697 (skip-syntax-backward " ")
698 (setq end (point))
2b86bfb1
SM
699 (when (> end start)
700 (setq name (buffer-substring start end))
701 (goto-char pos) ; Restore point.
702 (list (abbrev-symbol name tables) name start end)))
f57a9512 703
e047f448
SM
704 (while (and tables (not (car res)))
705 (let* ((table (pop tables))
706 (enable-fun (abbrev-table-get table :enable-function)))
707 (setq tables (append (abbrev-table-get table :parents) tables))
708 (setq res
709 (and (or (not enable-fun) (funcall enable-fun))
710 (looking-back (or (abbrev-table-get table :regexp)
711 "\\<\\(\\w+\\)\\W*")
712 (line-beginning-position))
713 (setq start (match-beginning 1))
714 (setq end (match-end 1))
79415279
SM
715 (setq name (buffer-substring start end))
716 (let ((abbrev (abbrev-symbol name table)))
717 (when abbrev
718 (setq enable-fun (abbrev-get abbrev :enable-function))
719 (and (or (not enable-fun) (funcall enable-fun))
720 ;; This will also look it up in parent tables.
721 ;; This is not on purpose, but it seems harmless.
722 (list abbrev name start end))))))
e047f448
SM
723 ;; Restore point.
724 (goto-char pos)))
725 res)))
726
727(defvar abbrev-expand-functions nil
728 "Wrapper hook around `expand-abbrev'.
729The functions on this special hook are called with one argument:
730a function that performs the abbrev expansion. It should return
731the abbrev symbol if expansion took place.")
732
733(defun expand-abbrev ()
734 "Expand the abbrev before point, if there is an abbrev there.
735Effective when explicitly called even when `abbrev-mode' is nil.
736Returns the abbrev symbol, if expansion took place."
737 (interactive)
738 (run-hooks 'pre-abbrev-expand-hook)
739 (abbrev-with-wrapper-hook abbrev-expand-functions
740 (destructuring-bind (&optional sym name wordstart wordend)
741 (abbrev--before-point)
742 (when sym
743 (let ((value sym))
744 (unless (or ;; executing-kbd-macro
745 noninteractive
746 (window-minibuffer-p (selected-window)))
747 ;; Add an undo boundary, in case we are doing this for
748 ;; a self-inserting command which has avoided making one so far.
749 (undo-boundary))
750 ;; Now sym is the abbrev symbol.
751 (setq last-abbrev-text name)
752 (setq last-abbrev sym)
753 (setq last-abbrev-location wordstart)
754 ;; Increment use count.
79415279 755 (abbrev-put sym :count (1+ (abbrev-get sym :count)))
e047f448
SM
756 ;; If this abbrev has an expansion, delete the abbrev
757 ;; and insert the expansion.
758 (when (stringp (symbol-value sym))
1449012d
SM
759 (goto-char wordstart)
760 ;; Insert at beginning so that markers at the end (e.g. point)
761 ;; are preserved.
e047f448 762 (insert (symbol-value sym))
1449012d 763 (delete-char (- wordend wordstart))
e047f448
SM
764 (let ((case-fold-search nil))
765 ;; If the abbrev's name is different from the buffer text (the
766 ;; only difference should be capitalization), then we may want
767 ;; to adjust the capitalization of the expansion.
768 (when (and (not (equal name (symbol-name sym)))
769 (string-match "[[:upper:]]" name))
770 (if (not (string-match "[[:lower:]]" name))
771 ;; Abbrev was all caps. If expansion is multiple words,
772 ;; normally capitalize each word.
773 (if (and (not abbrev-all-caps)
774 (save-excursion
775 (> (progn (backward-word 1) (point))
776 (progn (goto-char wordstart)
777 (forward-word 1) (point)))))
778 (upcase-initials-region wordstart (point))
779 (upcase-region wordstart (point)))
780 ;; Abbrev included some caps. Cap first initial of expansion.
781 (let ((end (point)))
782 ;; Find the initial.
783 (goto-char wordstart)
784 (skip-syntax-forward "^w" (1- end))
785 ;; Change just that.
1449012d
SM
786 (upcase-initials-region (point) (1+ (point)))
787 (goto-char end))))))
788 ;; Now point is at the end of the expansion and the beginning is
789 ;; in last-abbrev-location.
e047f448
SM
790 (when (symbol-function sym)
791 (let* ((hook (symbol-function sym))
792 (expanded
793 ;; If the abbrev has a hook function, run it.
794 (funcall hook)))
795 ;; In addition, if the hook function is a symbol with
796 ;; a non-nil `no-self-insert' property, let the value it
797 ;; returned specify whether we consider that an expansion took
798 ;; place. If it returns nil, no expansion has been done.
799 (if (and (symbolp hook)
800 (null expanded)
801 (get hook 'no-self-insert))
802 (setq value nil))))
803 value)))))
804
805(defun unexpand-abbrev ()
806 "Undo the expansion of the last abbrev that expanded.
807This differs from ordinary undo in that other editing done since then
808is not undone."
809 (interactive)
810 (save-excursion
811 (unless (or (< last-abbrev-location (point-min))
812 (> last-abbrev-location (point-max)))
813 (goto-char last-abbrev-location)
814 (when (stringp last-abbrev-text)
815 ;; This isn't correct if last-abbrev's hook was used
816 ;; to do the expansion.
817 (let ((val (symbol-value last-abbrev)))
818 (unless (stringp val)
819 (error "value of abbrev-symbol must be a string"))
820 (delete-region (point) (+ (point) (length val)))
821 ;; Don't inherit properties here; just copy from old contents.
822 (insert last-abbrev-text)
823 (setq last-abbrev-text nil))))))
824
825(defun abbrev--write (sym)
826 "Write the abbrev in a `read'able form.
827Only writes the non-system abbrevs.
828Presumes that `standard-output' points to `current-buffer'."
79415279 829 (unless (or (null (symbol-value sym)) (abbrev-get sym :system))
e047f448 830 (insert " (")
d548715c 831 (prin1 (symbol-name sym))
e047f448
SM
832 (insert " ")
833 (prin1 (symbol-value sym))
834 (insert " ")
835 (prin1 (symbol-function sym))
836 (insert " ")
79415279 837 (prin1 (abbrev-get sym :count))
e047f448
SM
838 (insert ")\n")))
839
840(defun abbrev--describe (sym)
841 (when (symbol-value sym)
842 (prin1 (symbol-name sym))
79415279 843 (if (null (abbrev-get sym :system))
e047f448
SM
844 (indent-to 15 1)
845 (insert " (sys)")
846 (indent-to 20 1))
79415279 847 (prin1 (abbrev-get sym :count))
e047f448
SM
848 (indent-to 20 1)
849 (prin1 (symbol-value sym))
850 (when (symbol-function sym)
851 (indent-to 45 1)
852 (prin1 (symbol-function sym)))
853 (terpri)))
854
855(defun insert-abbrev-table-description (name &optional readable)
856 "Insert before point a full description of abbrev table named NAME.
857NAME is a symbol whose value is an abbrev table.
858If optional 2nd arg READABLE is non-nil, a human-readable description
859is inserted. Otherwise the description is an expression,
860a call to `define-abbrev-table', which would
861define the abbrev table NAME exactly as it is currently defined.
862
863Abbrevs marked as \"system abbrevs\" are omitted."
864 (let ((table (symbol-value name))
865 (symbols ()))
866 (mapatoms (lambda (sym) (if (symbol-value sym) (push sym symbols))) table)
867 (setq symbols (sort symbols 'string-lessp))
868 (let ((standard-output (current-buffer)))
869 (if readable
870 (progn
871 (insert "(")
872 (prin1 name)
873 (insert ")\n\n")
874 (mapc 'abbrev--describe symbols)
875 (insert "\n\n"))
876 (insert "(define-abbrev-table '")
877 (prin1 name)
878 (insert " '(")
879 (mapc 'abbrev--write symbols)
880 (insert " ))\n\n"))
881 nil)))
882
883(defun define-abbrev-table (tablename definitions
884 &optional docstring &rest props)
885 "Define TABLENAME (a symbol) as an abbrev table name.
886Define abbrevs in it according to DEFINITIONS, which is a list of elements
887of the form (ABBREVNAME EXPANSION HOOK USECOUNT SYSTEMFLAG).
888\(If the list is shorter than that, omitted elements default to nil).
889PROPS is a property list to apply to the table.
890Properties with special meaning:
891- `:parents' contains a list of abbrev tables from which this table inherits
892 abbreviations.
893- `:case-fixed' non-nil means that abbreviations are looked up without
894 case-folding, and the expansion is not capitalized/upcased.
f57a9512 895- `:regexp' describes the form of abbrevs. It defaults to \\=\\<\\(\\w+\\)\\W* which
e047f448
SM
896 means that an abbrev can only be a single word. The submatch 1 is treated
897 as the potential name of an abbrev.
898- `:enable-function' can be set to a function of no argument which returns
899 non-nil iff the abbrevs in this table should be used for this instance
900 of `expand-abbrev'."
4eebd7fe
SM
901 ;; We used to manually add the docstring, but we also want to record this
902 ;; location as the definition of the variable (in load-history), so we may
903 ;; as well just use `defvar'.
904 (eval `(defvar ,tablename nil ,@(if (stringp docstring) (list docstring))))
e047f448
SM
905 (let ((table (if (boundp tablename) (symbol-value tablename))))
906 (unless table
907 (setq table (make-abbrev-table props))
908 (set tablename table)
909 (push tablename abbrev-table-name-list))
e047f448
SM
910 (dolist (elt definitions)
911 (apply 'define-abbrev table elt))))
912
c06d4c1f
RS
913(provide 'abbrev)
914
21a360b2 915;; arch-tag: dbd6f3ae-dfe3-40ba-b00f-f9e3ff960df5
c0274f38 916;;; abbrev.el ends here