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