(ELCFILES): Regenerate.
[bpt/emacs.git] / lisp / dabbrev.el
CommitLineData
7313ccdb 1;;; dabbrev.el --- dynamic abbreviation package
b578f267 2
0d30b337 3;; Copyright (C) 1985, 1986, 1992, 1994, 1996, 1997, 2000, 2001, 2002,
f3ae2521
GM
4;; 2003, 2004, 2005, 2006, 2007, 2008, 2009
5;; Free Software Foundation, Inc.
2f790b20 6
6163b3ba 7;; Author: Don Morrison
f3ae2521
GM
8;; Lars Lindberg
9;; (according to ack.texi)
6163b3ba
RS
10;; Maintainer: Lars Lindberg <Lars.Lindberg@sypro.cap.se>
11;; Created: 16 Mars 1992
df6eb420 12;; Lindberg's last update version: 5.7
f5f727f8 13;; Keywords: abbrev expand completion convenience
3a801d0c 14
b578f267
EN
15;; This file is part of GNU Emacs.
16
eb3fa2cf 17;; GNU Emacs is free software: you can redistribute it and/or modify
2f790b20 18;; it under the terms of the GNU General Public License as published by
eb3fa2cf
GM
19;; the Free Software Foundation, either version 3 of the License, or
20;; (at your option) any later version.
b578f267
EN
21
22;; GNU Emacs is distributed in the hope that it will be useful,
2f790b20
JB
23;; but WITHOUT ANY WARRANTY; without even the implied warranty of
24;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
25;; GNU General Public License for more details.
b578f267 26
2f790b20 27;; You should have received a copy of the GNU General Public License
eb3fa2cf 28;; along with GNU Emacs. If not, see <http://www.gnu.org/licenses/>.
2f790b20 29
e5167999 30;;; Commentary:
2f790b20 31
6163b3ba
RS
32;; The purpose with this package is to let you write just a few
33;; characters of words you've written earlier to be able to expand
34;; them.
35;;
36;; To expand a word, just put the point right after the word and press
37;; M-/ (dabbrev-expand) or M-C-/ (dabbrev-completion).
38;;
6163b3ba
RS
39;; Check out the customizable variables below to learn about all the
40;; features of this package.
41
42;;; Hints and tips for major modes writers:
43
44;; Recommended values C/Lisp etc text
45;; dabbrev-case-fold-search nil t
46;; dabbrev-case-replace nil t
47;;
48;; Set the variables you want special for your mode like this:
49;; (set (make-local-variable 'dabbrev-case-replace) nil)
a7acbbe4 50;; Then you don't interfere with other modes.
6163b3ba
RS
51;;
52;; If your mode handles buffers that refers to other buffers
53;; (i.e. compilation-mode, gud-mode), then try to set
54;; `dabbrev-select-buffers-function' or `dabbrev-friend-buffer-function'
55;; to a function that point out those buffers.
56
57;; Same goes for major-modes that are connected to other modes. There
58;; are for instance a number of mail-modes. One for reading, one for
59;; creating a new mail etc. Maybe those should be connected.
60
61;; Example for GNUS (when we write a reply, we want dabbrev to look in
62;; the article for expansion):
63;; (set (make-local-variable 'dabbrev-friend-buffer-function)
64;; (lambda (buffer)
65;; (save-excursion
66;; (set-buffer buffer)
67;; (memq major-mode '(news-reply-mode gnus-article-mode)))))
68
6163b3ba
RS
69
70;; Known bugs and limitations.
71;; - Possible to do several levels of `dabbrev-completion' in the
72;; minibuffer.
73;; - dabbrev-completion doesn't handle resetting the globals variables
74;; right. It resets them after finding the abbrev.
75
76;; Future enhancements
77;; - Check the tags-files? Like tags-complete?
78;; - Add the possibility of searching both forward and backward to
79;; the nearest expansion.
7313ccdb
RS
80;; - Check the kill-ring when everything else fails. (Maybe something
81;; for hippie-expand?). [Bng] <boris@cs.rochester.edu>
6163b3ba 82
df6eb420 83;;; These people gave suggestions:
6163b3ba
RS
84;; [hymie] Hyman Rosen <marks!hymie@jyacc.jyacc.com>
85;; [burgett] Steve Burgett <burgett@bizet.eecs.berkeley.edu>
86;; [jules] Julian Gosnell <jules@x.co.uk>
87;; [kifer] Michael Kifer <kifer@sbcs.sunysb.edu>
88;; [ake] Ake Stenhoff <extaksf@aom.ericsson.se>
89;; [alon] Alon Albert <al%imercury@uunet.uu.net>
90;; [tromey] Tom Tromey <tromey@busco.lanl.gov>
91;; [Rolf] Rolf Schreiber <rolf@mathematik.uni-stuttgart.de>
92;; [Petri] Petri Raitio <per@tekla.fi>
7bd9ba70 93;; [ejb] Jay Berkenbilt <ejb@ql.org>
6163b3ba
RS
94;; [hawley] Bob Hawley <rth1@quartet.mt.att.com>
95;; ... and to all the people who have participated in the beta tests.
2f790b20 96
e5167999 97;;; Code:
6163b3ba 98
b578f267
EN
99;;----------------------------------------------------------------
100;; Customization variables
101;;----------------------------------------------------------------
6163b3ba 102
bbf5eb28 103(defgroup dabbrev nil
a6bd541a 104 "Dynamic Abbreviations."
bbf5eb28 105 :tag "Dynamic Abbreviations"
f5f727f8
DN
106 :group 'abbrev
107 :group 'convenience)
6163b3ba 108
bbf5eb28 109(defcustom dabbrev-backward-only nil
9201cc28 110 "If non-nil, `dabbrev-expand' only looks backwards."
bbf5eb28
RS
111 :type 'boolean
112 :group 'dabbrev)
113
114(defcustom dabbrev-limit nil
9201cc28 115 "Limits region searched by `dabbrev-expand' to this many chars away."
bbf5eb28
RS
116 :type '(choice (const :tag "off" nil)
117 integer)
118 :group 'dabbrev)
119
120(defcustom dabbrev-abbrev-skip-leading-regexp nil
9201cc28 121 "Regexp for skipping leading characters of an abbreviation.
6163b3ba 122
7313ccdb
RS
123Example: Set this to \"\\\\$\" for programming languages
124in which variable names may appear with or without a leading `$'.
79bb4872 125\(For example, in Makefiles.\)
6163b3ba 126
bbf5eb28
RS
127Set this to nil if no characters should be skipped."
128 :type '(choice regexp
129 (const :tag "off" nil))
130 :group 'dabbrev)
6163b3ba 131
e2e75068 132(defcustom dabbrev-eliminate-newlines t
9201cc28 133 "Non-nil means dabbrev should not insert newlines.
fd2dfb40
RS
134Instead it converts them to spaces."
135 :type 'boolean
136 :group 'dabbrev)
137
bbf5eb28 138(defcustom dabbrev-case-fold-search 'case-fold-search
9201cc28 139 "Control whether dabbrev searches should ignore case.
7313ccdb 140A value of nil means case is significant.
543abb4a
RS
141A value of `case-fold-search' means case is significant
142 if `case-fold-search' is nil.
143Any other non-nil version means case is not significant."
144 :type '(choice (const :tag "off" nil)
eaaca5ee
AS
145 (const :tag "like search" case-fold-search)
146 (other :tag "on" t))
bbf5eb28 147 :group 'dabbrev)
6dc3311d 148;;;###autoload(put 'dabbrev-case-fold-search 'risky-local-variable t)
6163b3ba 149
bbf5eb28 150(defcustom dabbrev-upcase-means-case-search nil
9201cc28 151 "The significance of an uppercase character in an abbreviation.
602ea79e 152A nil value means case fold search when searching for possible expansions;
93a43334 153non-nil means case sensitive search.
6163b3ba 154
7313ccdb 155This variable has an effect only when the value of
543abb4a 156`dabbrev-case-fold-search' says to ignore case."
bbf5eb28
RS
157 :type 'boolean
158 :group 'dabbrev)
6163b3ba 159
93a43334 160(defcustom dabbrev-case-distinction 'case-replace
9201cc28 161 "Whether dabbrev treats expansions as the same if they differ in case.
93a43334
RS
162
163A value of nil means treat them as different.
164A value of `case-replace' means distinguish them if `case-replace' is nil.
165Any other non-nil value means to treat them as the same.
166
167This variable has an effect only when the value of
168`dabbrev-case-fold-search' specifies to ignore case."
169 :type '(choice (const :tag "off" nil)
170 (const :tag "based on `case-replace'" case-replace)
171 (other :tag "on" t))
172 :group 'dabbrev
bf247b6e 173 :version "22.1")
93a43334 174
bbf5eb28 175(defcustom dabbrev-case-replace 'case-replace
9201cc28 176 "Whether dabbrev applies the abbreviations's case pattern to the expansion.
93a43334
RS
177
178A value of nil means preserve the expansion's case pattern.
179A value of `case-replace' means preserve it if `case-replace' is nil.
180Any other non-nil value means modify the expansion
181by applying the abbreviation's case pattern to it.
6163b3ba 182
7313ccdb 183This variable has an effect only when the value of
543abb4a
RS
184`dabbrev-case-fold-search' specifies to ignore case."
185 :type '(choice (const :tag "off" nil)
93a43334 186 (const :tag "based on `case-replace'" case-replace)
eaaca5ee 187 (other :tag "on" t))
bbf5eb28 188 :group 'dabbrev)
6dc3311d 189;;;###autoload(put 'dabbrev-case-replace 'risky-local-variable t)
6163b3ba 190
bbf5eb28 191(defcustom dabbrev-abbrev-char-regexp nil
9201cc28 192 "Regexp to recognize a character in an abbreviation or expansion.
7313ccdb 193This regexp will be surrounded with \\\\( ... \\\\) when actually used.
6163b3ba 194
7313ccdb 195Set this variable to \"\\\\sw\" if you want ordinary words or
df6eb420
RS
196\"\\\\sw\\\\|\\\\s_\" if you want symbols (including characters whose
197syntax is \"symbol\" as well as those whose syntax is \"word\".
6163b3ba 198
df6eb420
RS
199The value nil has a special meaning: the abbreviation is from point to
200previous word-start, but the search is for symbols.
6163b3ba 201
7313ccdb 202For instance, if you are programming in Lisp, `yes-or-no-p' is a symbol,
df6eb420
RS
203while `yes', `or', `no' and `p' are considered words. If this
204variable is nil, then expanding `yes-or-no-' looks for a symbol
7313ccdb
RS
205starting with or containing `no-'. If you set this variable to
206\"\\\\sw\\\\|\\\\s_\", that expansion looks for a symbol starting with
207`yes-or-no-'. Finally, if you set this variable to \"\\\\sw\", then
208expanding `yes-or-no-' signals an error because `-' is not part of a word;
209but expanding `yes-or-no' looks for a word starting with `no'.
6163b3ba 210
bbf5eb28
RS
211The recommended value is \"\\\\sw\\\\|\\\\s_\"."
212 :type '(choice (const nil)
213 regexp)
214 :group 'dabbrev)
6163b3ba 215
bbf5eb28 216(defcustom dabbrev-check-all-buffers t
9201cc28 217 "Non-nil means dabbrev package should search *all* buffers.
6163b3ba 218
df6eb420
RS
219Dabbrev always searches the current buffer first. Then, if
220`dabbrev-check-other-buffers' says so, it searches the buffers
221designated by `dabbrev-select-buffers-function'.
6163b3ba 222
df6eb420 223Then, if `dabbrev-check-all-buffers' is non-nil, dabbrev searches
7e65cba2
GM
224all the other buffers, except those named in `dabbrev-ignored-buffer-names',
225or matched by `dabbrev-ignored-regexps'."
bbf5eb28
RS
226 :type 'boolean
227 :group 'dabbrev)
df6eb420 228
9648a1e6 229(defcustom dabbrev-ignored-buffer-names '("*Messages*" "*Buffer List*")
9201cc28 230 "List of buffer names that dabbrev should not check.
c4ca64db 231See also `dabbrev-ignored-buffer-regexps'."
7bd9ba70 232 :type '(repeat (string :tag "Buffer name"))
cd32a7ba
DN
233 :group 'dabbrev
234 :version "20.3")
7bd9ba70 235
c4ca64db 236(defcustom dabbrev-ignored-buffer-regexps nil
9201cc28 237 "List of regexps matching names of buffers that dabbrev should not check.
7e65cba2
GM
238See also `dabbrev-ignored-buffer-names'."
239 :type '(repeat regexp)
240 :group 'dabbrev
241 :version "21.1")
242
bbf5eb28 243(defcustom dabbrev-check-other-buffers t
9201cc28 244 "Should \\[dabbrev-expand] look in other buffers?\
6163b3ba 245
df6eb420
RS
246nil: Don't look in other buffers.
247t: Also look for expansions in the buffers pointed out by
248 `dabbrev-select-buffers-function'.
249Anything else: When we can't find any more expansions in
250the current buffer, then ask the user whether to look in other
251buffers too.
252
bbf5eb28
RS
253The default value is t."
254 :type '(choice (const :tag "off" nil)
255 (const :tag "on" t)
eaaca5ee 256 (other :tag "ask" other))
bbf5eb28 257 :group 'dabbrev)
6163b3ba
RS
258
259;; I guess setting this to a function that selects all C- or C++-
260;; mode buffers would be a good choice for a debugging buffer,
261;; when debugging C- or C++-code.
262(defvar dabbrev-select-buffers-function 'dabbrev--select-buffers
263 "A function that selects buffers that should be searched by dabbrev.
6163b3ba 264The function should take no arguments and return a list of buffers to
05be3833
RS
265search for expansions. See the source of `dabbrev--select-buffers'
266for an example.
6163b3ba
RS
267
268A mode setting this variable should make it buffer local.")
269
bbf5eb28 270(defcustom dabbrev-friend-buffer-function 'dabbrev--same-major-mode-p
9201cc28 271 "A function to decide whether dabbrev should search OTHER-BUFFER.
6163b3ba
RS
272The function should take one argument, OTHER-BUFFER, and return
273non-nil if that buffer should be searched. Have a look at
274`dabbrev--same-major-mode-p' for an example.
275
7313ccdb
RS
276The value of `dabbrev-friend-buffer-function' has an effect only if
277the value of `dabbrev-select-buffers-function' uses it. The function
278`dabbrev--select-buffers' is one function you can use here.
6163b3ba 279
bbf5eb28
RS
280A mode setting this variable should make it buffer local."
281 :type 'function
282 :group 'dabbrev)
6163b3ba 283
bbf5eb28 284(defcustom dabbrev-search-these-buffers-only nil
7313ccdb
RS
285 "If non-nil, a list of buffers which dabbrev should search.
286If this variable is non-nil, dabbrev will only look in these buffers.
287It will not even look in the current buffer if it is not a member of
f5307782
JB
288this list."
289 :group 'dabbrev)
e5167999 290
b578f267
EN
291;;----------------------------------------------------------------
292;; Internal variables
293;;----------------------------------------------------------------
2f790b20 294
6163b3ba
RS
295;; Last obarray of completions in `dabbrev-completion'
296(defvar dabbrev--last-obarray nil)
2f790b20 297
6163b3ba
RS
298;; Table of expansions seen so far
299(defvar dabbrev--last-table nil)
2f790b20 300
6163b3ba
RS
301;; Last string we tried to expand.
302(defvar dabbrev--last-abbreviation nil)
2f790b20 303
6163b3ba
RS
304;; Location last abbreviation began
305(defvar dabbrev--last-abbrev-location nil)
2f790b20 306
6163b3ba
RS
307;; Direction of last dabbrevs search
308(defvar dabbrev--last-direction 0)
2f790b20 309
6163b3ba
RS
310;; Last expansion of an abbreviation.
311(defvar dabbrev--last-expansion nil)
2f790b20 312
6163b3ba
RS
313;; Location the last expansion was found.
314(defvar dabbrev--last-expansion-location nil)
315
316;; The list of remaining buffers with the same mode as current buffer.
317(defvar dabbrev--friend-buffer-list nil)
318
513e7954 319;; The buffer we looked in last, not counting the current buffer.
6163b3ba
RS
320(defvar dabbrev--last-buffer nil)
321
322;; The buffer we found the expansion last time.
323(defvar dabbrev--last-buffer-found nil)
324
325;; The buffer we last did a completion in.
326(defvar dabbrev--last-completion-buffer nil)
327
3ffa545b
GM
328;; If non-nil, a function to use when copying successive words.
329;; It should be `upcase' or `downcase'.
dea5efcb
RS
330(defvar dabbrev--last-case-pattern nil)
331
df6eb420
RS
332;; Same as dabbrev-check-other-buffers, but is set for every expand.
333(defvar dabbrev--check-other-buffers dabbrev-check-other-buffers)
6163b3ba
RS
334
335;; The regexp for recognizing a character in an abbreviation.
336(defvar dabbrev--abbrev-char-regexp nil)
337
7daa3523
TTN
338;; The progress reporter for buffer-scanning progress.
339(defvar dabbrev--progress-reporter nil)
340
b578f267
EN
341;;----------------------------------------------------------------
342;; Macros
343;;----------------------------------------------------------------
6163b3ba
RS
344
345;;; Get the buffer that mini-buffer was activated from
346(defsubst dabbrev--minibuffer-origin ()
347 (car (cdr (buffer-list))))
348
7313ccdb
RS
349;; Make a list of some of the elements of LIST.
350;; Check each element of LIST, storing it temporarily in the
351;; variable ELEMENT, and include it in the result
352;; if CONDITION evaluates non-nil.
353(defmacro dabbrev-filter-elements (element list condition)
da49057c
SS
354 `(let (dabbrev-result dabbrev-tail ,element)
355 (setq dabbrev-tail ,list)
356 (while dabbrev-tail
357 (setq ,element (car dabbrev-tail))
358 (if ,condition
359 (setq dabbrev-result (cons ,element dabbrev-result)))
360 (setq dabbrev-tail (cdr dabbrev-tail)))
361 (nreverse dabbrev-result)))
7313ccdb 362
b578f267
EN
363;;----------------------------------------------------------------
364;; Exported functions
365;;----------------------------------------------------------------
6163b3ba 366
a1ff29b9 367;;;###autoload (define-key esc-map "/" 'dabbrev-expand)
7313ccdb 368;;;??? Do we want this?
a1ff29b9 369;;;###autoload (define-key esc-map [?\C-/] 'dabbrev-completion)
6163b3ba
RS
370
371;;;###autoload
372(defun dabbrev-completion (&optional arg)
373 "Completion on current word.
6163b3ba
RS
374Like \\[dabbrev-expand] but finds all expansions in the current buffer
375and presents suggestions for completion.
376
7313ccdb
RS
377With a prefix argument, it searches all buffers accepted by the
378function pointed out by `dabbrev-friend-buffer-function' to find the
dd1ae355
RS
379completions.
380
381If the prefix argument is 16 (which comes from C-u C-u),
fe0e0a47 382then it searches *all* buffers."
6163b3ba 383 (interactive "*P")
df6eb420
RS
384 (dabbrev--reset-global-variables)
385 (let* ((dabbrev-check-other-buffers (and arg t))
386 (dabbrev-check-all-buffers
dd1ae355 387 (and arg (= (prefix-numeric-value arg) 16)))
6163b3ba 388 (abbrev (dabbrev--abbrev-at-point))
543abb4a
RS
389 (ignore-case-p (and (if (eq dabbrev-case-fold-search 'case-fold-search)
390 case-fold-search
391 dabbrev-case-fold-search)
392 (or (not dabbrev-upcase-means-case-search)
393 (string= abbrev (downcase abbrev)))))
6163b3ba
RS
394 (my-obarray dabbrev--last-obarray)
395 init)
396 (save-excursion
fe0e0a47
LT
397 ;;--------------------------------
398 ;; New abbreviation to expand.
399 ;;--------------------------------
400 (setq dabbrev--last-abbreviation abbrev)
401 ;; Find all expansion
402 (let ((completion-list
403 (dabbrev--find-all-expansions abbrev ignore-case-p))
404 (completion-ignore-case ignore-case-p))
405 ;; Make an obarray with all expansions
406 (setq my-obarray (make-vector (length completion-list) 0))
407 (or (> (length my-obarray) 0)
408 (error "No dynamic expansion for \"%s\" found%s"
409 abbrev
410 (if dabbrev--check-other-buffers "" " in this-buffer")))
411 (cond
412 ((or (not ignore-case-p)
413 (not dabbrev-case-replace))
414 (mapc (function (lambda (string)
415 (intern string my-obarray)))
416 completion-list))
417 ((string= abbrev (upcase abbrev))
418 (mapc (function (lambda (string)
419 (intern (upcase string) my-obarray)))
420 completion-list))
421 ((string= (substring abbrev 0 1)
422 (upcase (substring abbrev 0 1)))
423 (mapc (function (lambda (string)
424 (intern (capitalize string) my-obarray)))
425 completion-list))
426 (t
427 (mapc (function (lambda (string)
428 (intern (downcase string) my-obarray)))
429 completion-list)))
430 (setq dabbrev--last-obarray my-obarray)
431 (setq dabbrev--last-completion-buffer (current-buffer))
432 ;; Find the longest common string.
433 (setq init (try-completion abbrev my-obarray))))
6163b3ba
RS
434 ;;--------------------------------
435 ;; Let the user choose between the expansions
436 ;;--------------------------------
437 (or (stringp init)
438 (setq init abbrev))
439 (cond
440 ;; * Replace string fragment with matched common substring completion.
441 ((and (not (string-equal init ""))
442 (not (string-equal (downcase init) (downcase abbrev))))
443 (if (> (length (all-completions init my-obarray)) 1)
7313ccdb
RS
444 (message "Repeat `%s' to see all completions"
445 (key-description (this-command-keys)))
6163b3ba 446 (message "The only possible completion"))
3ffa545b 447 (dabbrev--substitute-expansion nil abbrev init nil))
6163b3ba
RS
448 (t
449 ;; * String is a common substring completion already. Make list.
450 (message "Making completion list...")
ecd91f5f 451 (with-output-to-temp-buffer "*Completions*"
f5fab556
MY
452 (display-completion-list (all-completions init my-obarray)
453 init))
7313ccdb 454 (message "Making completion list...done")))
6163b3ba
RS
455 (and (window-minibuffer-p (selected-window))
456 (message nil))))
2f790b20
JB
457
458;;;###autoload
459(defun dabbrev-expand (arg)
460 "Expand previous word \"dynamically\".
2f790b20 461
6163b3ba
RS
462Expands to the most recent, preceding word for which this is a prefix.
463If no suitable preceding word is found, words following point are
464considered. If still no suitable word is found, then look in the
465buffers accepted by the function pointed out by variable
466`dabbrev-friend-buffer-function'.
2f790b20 467
7313ccdb 468A positive prefix argument, N, says to take the Nth backward *distinct*
6163b3ba 469possibility. A negative argument says search forward.
2f790b20
JB
470
471If the cursor has not moved from the end of the previous expansion and
472no argument is given, replace the previously-made expansion
6163b3ba
RS
473with the next possible expansion not yet tried.
474
475The variable `dabbrev-backward-only' may be used to limit the
476direction of search to backward if set non-nil.
477
7313ccdb 478See also `dabbrev-abbrev-char-regexp' and \\[dabbrev-completion]."
2f790b20 479 (interactive "*P")
dea5efcb
RS
480 (let (abbrev record-case-pattern
481 expansion old direction (orig-point (point)))
2f790b20
JB
482 ;; abbrev -- the abbrev to expand
483 ;; expansion -- the expansion found (eventually) or nil until then
484 ;; old -- the text currently in the buffer
485 ;; (the abbrev, or the previously-made expansion)
2f790b20
JB
486 (save-excursion
487 (if (and (null arg)
df6eb420
RS
488 (markerp dabbrev--last-abbrev-location)
489 (marker-position dabbrev--last-abbrev-location)
6163b3ba
RS
490 (or (eq last-command this-command)
491 (and (window-minibuffer-p (selected-window))
492 (= dabbrev--last-abbrev-location
493 (point)))))
7313ccdb 494 ;; Find a different expansion for the same abbrev as last time.
6163b3ba
RS
495 (progn
496 (setq abbrev dabbrev--last-abbreviation)
497 (setq old dabbrev--last-expansion)
498 (setq direction dabbrev--last-direction))
a8a2d6ca
KH
499 ;; If the user inserts a space after expanding
500 ;; and then asks to expand again, always fetch the next word.
a6bd541a 501 (if (and (eq (preceding-char) ?\s)
a8a2d6ca
KH
502 (markerp dabbrev--last-abbrev-location)
503 (marker-position dabbrev--last-abbrev-location)
504 (= (point) (1+ dabbrev--last-abbrev-location)))
dea5efcb 505 (progn
a8a2d6ca
KH
506 ;; The "abbrev" to expand is just the space.
507 (setq abbrev " ")
508 (save-excursion
b9559a72
RS
509 (save-restriction
510 (widen)
511 (if dabbrev--last-buffer
512 (set-buffer dabbrev--last-buffer))
513 ;; Find the end of the last "expansion" word.
514 (if (or (eq dabbrev--last-direction 1)
515 (and (eq dabbrev--last-direction 0)
516 (< dabbrev--last-expansion-location (point))))
517 (setq dabbrev--last-expansion-location
518 (+ dabbrev--last-expansion-location
519 (length dabbrev--last-expansion))))
520 (goto-char dabbrev--last-expansion-location)
521 ;; Take the following word, with intermediate separators,
522 ;; as our expansion this time.
523 (re-search-forward
524 (concat "\\(?:" dabbrev--abbrev-char-regexp "\\)+"))
525 (setq expansion (buffer-substring-no-properties
526 dabbrev--last-expansion-location (point)))
527
528 ;; Record the end of this expansion, in case we repeat this.
529 (setq dabbrev--last-expansion-location (point))))
a8a2d6ca
KH
530 ;; Indicate that dabbrev--last-expansion-location is
531 ;; at the end of the expansion.
532 (setq dabbrev--last-direction -1))
533
534 ;; We have a different abbrev to expand.
535 (dabbrev--reset-global-variables)
536 (setq direction (if (null arg)
537 (if dabbrev-backward-only 1 0)
538 (prefix-numeric-value arg)))
539 (setq abbrev (dabbrev--abbrev-at-point))
dea5efcb 540 (setq record-case-pattern t)
a8a2d6ca 541 (setq old nil)))
6163b3ba
RS
542
543 ;;--------------------------------
544 ;; Find the expansion
545 ;;--------------------------------
a8a2d6ca
KH
546 (or expansion
547 (setq expansion
548 (dabbrev--find-expansion abbrev direction
543abb4a
RS
549 (and (if (eq dabbrev-case-fold-search 'case-fold-search)
550 case-fold-search
551 dabbrev-case-fold-search)
a8a2d6ca
KH
552 (or (not dabbrev-upcase-means-case-search)
553 (string= abbrev (downcase abbrev))))))))
6163b3ba
RS
554 (cond
555 ((not expansion)
556 (dabbrev--reset-global-variables)
557 (if old
558 (save-excursion
4209f479 559 (setq buffer-undo-list (cons orig-point buffer-undo-list))
3132e115
RS
560 ;; Put back the original abbrev with its original case pattern.
561 (search-backward old)
562 (insert abbrev)
563 (delete-region (point) (+ (point) (length old)))))
7313ccdb 564 (error "No%s dynamic expansion for `%s' found"
6163b3ba
RS
565 (if old " further" "") abbrev))
566 (t
fdad0f14
GM
567 (if (not (or (eq dabbrev--last-buffer dabbrev--last-buffer-found)
568 (minibuffer-window-active-p (selected-window))))
2f790b20 569 (progn
6163b3ba
RS
570 (message "Expansion found in '%s'"
571 (buffer-name dabbrev--last-buffer))
572 (setq dabbrev--last-buffer-found dabbrev--last-buffer))
573 (message nil))
a8a2d6ca
KH
574 (if (and (or (eq (current-buffer) dabbrev--last-buffer)
575 (null dabbrev--last-buffer))
576 (numberp dabbrev--last-expansion-location)
577 (and (> dabbrev--last-expansion-location (point))))
578 (setq dabbrev--last-expansion-location
579 (copy-marker dabbrev--last-expansion-location)))
2f790b20 580 ;; Success: stick it in and return.
4209f479 581 (setq buffer-undo-list (cons orig-point buffer-undo-list))
3ffa545b
GM
582 (dabbrev--substitute-expansion old abbrev expansion
583 record-case-pattern)
dea5efcb 584
2f790b20 585 ;; Save state for re-expand.
da49057c 586 (setq dabbrev--last-expansion expansion)
6163b3ba
RS
587 (setq dabbrev--last-abbreviation abbrev)
588 (setq dabbrev--last-abbrev-location (point-marker))))))
589
b578f267
EN
590;;----------------------------------------------------------------
591;; Local functions
592;;----------------------------------------------------------------
6163b3ba 593
6163b3ba
RS
594;;; Checks if OTHER-BUFFER has the same major mode as current buffer.
595(defun dabbrev--same-major-mode-p (other-buffer)
7313ccdb
RS
596 (eq major-mode
597 (save-excursion
598 (set-buffer other-buffer)
599 major-mode)))
6163b3ba
RS
600
601;;; Back over all abbrev type characters and then moves forward over
602;;; all skip characters.
603(defun dabbrev--goto-start-of-abbrev ()
604 ;; Move backwards over abbrev chars
605 (save-match-data
b0021416
RS
606 (when (> (point) (minibuffer-prompt-end))
607 (forward-char -1)
608 (while (and (looking-at dabbrev--abbrev-char-regexp)
609 (> (point) (minibuffer-prompt-end))
610 (not (= (point) (field-beginning (point) nil
611 (1- (point))))))
612 (forward-char -1))
613 (or (looking-at dabbrev--abbrev-char-regexp)
614 (forward-char 1)))
6163b3ba
RS
615 (and dabbrev-abbrev-skip-leading-regexp
616 (while (looking-at dabbrev-abbrev-skip-leading-regexp)
617 (forward-char 1)))))
618
7313ccdb 619;;; Extract the symbol at point to serve as abbreviation.
6163b3ba
RS
620(defun dabbrev--abbrev-at-point ()
621 ;; Check for error
a8a2d6ca
KH
622 (if (bobp)
623 (error "No possible abbreviation preceding point"))
6163b3ba
RS
624 ;; Return abbrev at point
625 (save-excursion
a8a2d6ca 626 ;; Record the end of the abbreviation.
6163b3ba 627 (setq dabbrev--last-abbrev-location (point))
a8a2d6ca
KH
628 ;; If we aren't right after an abbreviation,
629 ;; move point back to just after one.
630 ;; This is so the user can get successive words
631 ;; by typing the punctuation followed by M-/.
632 (save-match-data
633 (if (save-excursion
634 (forward-char -1)
635 (not (looking-at (concat "\\("
636 (or dabbrev-abbrev-char-regexp
637 "\\sw\\|\\s_")
638 "\\)+"))))
639 (if (re-search-backward (or dabbrev-abbrev-char-regexp
640 "\\sw\\|\\s_")
641 nil t)
642 (forward-char 1)
643 (error "No possible abbreviation preceding point"))))
644 ;; Now find the beginning of that one.
645 (dabbrev--goto-start-of-abbrev)
6f0b000c
RS
646 (buffer-substring-no-properties
647 dabbrev--last-abbrev-location (point))))
da49057c 648
6163b3ba
RS
649;;; Initializes all global variables
650(defun dabbrev--reset-global-variables ()
651 ;; dabbrev--last-obarray and dabbrev--last-completion-buffer
652 ;; must not be reset here.
653 (setq dabbrev--last-table nil
654 dabbrev--last-abbreviation nil
655 dabbrev--last-abbrev-location nil
656 dabbrev--last-direction nil
657 dabbrev--last-expansion nil
658 dabbrev--last-expansion-location nil
659 dabbrev--friend-buffer-list nil
660 dabbrev--last-buffer nil
661 dabbrev--last-buffer-found nil
662 dabbrev--abbrev-char-regexp (or dabbrev-abbrev-char-regexp
663 "\\sw\\|\\s_")
df6eb420 664 dabbrev--check-other-buffers dabbrev-check-other-buffers))
6163b3ba 665
6163b3ba 666(defun dabbrev--select-buffers ()
513e7954
RS
667 "Return a list of other buffers to search for a possible abbrev.
668The current buffer is not included in the list.
669
670This function makes a list of all the buffers returned by `buffer-list',
671then discards buffers whose names match `dabbrev-ignored-buffer-names'
672or `dabbrev-ignored-buffer-regexps'. It also discards buffers for which
673`dabbrev-friend-buffer-function', if it is bound, returns nil when called
674with the buffer as argument.
675It returns the list of the buffers that are not discarded."
676 (dabbrev-filter-elements
677 buffer (buffer-list)
678 (and (not (eq (current-buffer) buffer))
679 (not (dabbrev--ignore-buffer-p buffer))
680 (boundp 'dabbrev-friend-buffer-function)
2ebf8f54 681 (funcall dabbrev-friend-buffer-function buffer))))
7313ccdb 682
6163b3ba 683(defun dabbrev--try-find (abbrev reverse n ignore-case)
fd2dfb40
RS
684 "Search for ABBREV, backwards if REVERSE, N times.
685If IGNORE-CASE is non-nil, ignore case while searching.
686Return the expansion found, and save the location of the start
687of the expansion in `dabbrev--last-expansion-location'."
6163b3ba 688 (save-excursion
df6eb420
RS
689 (save-restriction
690 (widen)
691 (let ((expansion nil))
692 (and dabbrev--last-expansion-location
693 (goto-char dabbrev--last-expansion-location))
694 (let ((case-fold-search ignore-case)
695 (count n))
696 (while (and (> count 0)
697 (setq expansion (dabbrev--search abbrev
698 reverse
93a43334
RS
699 (and ignore-case
700 (if (eq dabbrev-case-distinction 'case-replace)
701 case-replace
702 dabbrev-case-distinction))
703 )))
df6eb420
RS
704 (setq count (1- count))))
705 (and expansion
706 (setq dabbrev--last-expansion-location (point)))
707 expansion))))
6163b3ba 708
6163b3ba 709(defun dabbrev--find-all-expansions (abbrev ignore-case)
fd2dfb40
RS
710 "Return a list of all possible expansions of ABBREV.
711If IGNORE-CASE is non-nil, accept matches which differ in case."
6163b3ba
RS
712 (let ((all-expansions nil)
713 expansion)
714 (save-excursion
715 (goto-char (point-min))
716 (while (setq expansion (dabbrev--find-expansion abbrev -1 ignore-case))
df6eb420 717 (setq all-expansions (cons expansion all-expansions))))
6163b3ba
RS
718 all-expansions))
719
a6a06429
MB
720(defun dabbrev--ignore-buffer-p (buffer)
721 "Return non-nil if BUFFER should be ignored by dabbrev."
722 (let ((bn (buffer-name buffer)))
723 (or (member bn dabbrev-ignored-buffer-names)
724 (let ((tail dabbrev-ignored-buffer-regexps)
725 (match nil))
726 (while (and tail (not match))
727 (setq match (string-match (car tail) bn)
728 tail (cdr tail)))
729 match))))
730
6163b3ba 731(defun dabbrev--find-expansion (abbrev direction ignore-case)
fd2dfb40
RS
732 "Find one occurrence of ABBREV, and return the expansion.
733DIRECTION > 0 means look that many times backwards.
734DIRECTION < 0 means look that many times forward.
735DIRECTION = 0 means try both backward and forward.
736IGNORE-CASE non-nil means ignore case when searching.
737This sets `dabbrev--last-direction' to 1 or -1 according
738to the direction in which the occurrence was actually found.
71296446 739It sets `dabbrev--last-expansion-location' to the location
fd2dfb40 740of the start of the occurrence."
513e7954
RS
741 (save-excursion
742 ;; If we were scanning something other than the current buffer,
743 ;; continue scanning there.
744 (when dabbrev--last-buffer
7daa3523 745 (set-buffer dabbrev--last-buffer))
513e7954
RS
746 (or
747 ;; ------------------------------------------
748 ;; Look backward in current buffer.
749 ;; ------------------------------------------
750 (and (not dabbrev-search-these-buffers-only)
751 (>= direction 0)
752 (setq dabbrev--last-direction (min 1 direction))
753 (dabbrev--try-find abbrev t
754 (max 1 direction)
755 ignore-case))
756 ;; ------------------------------------------
757 ;; Look forward in current buffer
758 ;; or whatever buffer we were last scanning.
759 ;; ------------------------------------------
760 (and (or (not dabbrev-search-these-buffers-only)
761 dabbrev--last-buffer)
762 (<= direction 0)
763 (setq dabbrev--last-direction -1)
764 (dabbrev--try-find abbrev nil
765 (max 1 (- direction))
766 ignore-case))
767 ;; ------------------------------------------
768 ;; Look in other buffers.
769 ;; Always start at (point-min) and look forward.
770 ;; ------------------------------------------
771 (progn
772 (setq dabbrev--last-direction -1)
773 (unless dabbrev--last-buffer
774 ;; If we have just now begun to search other buffers,
775 ;; determine which other buffers we should check.
776 ;; Put that list in dabbrev--friend-buffer-list.
7daa3523
TTN
777 (unless dabbrev--friend-buffer-list
778 (setq dabbrev--friend-buffer-list
779 (dabbrev--make-friend-buffer-list))
780 (setq dabbrev--progress-reporter
781 (make-progress-reporter
782 "Scanning for dabbrevs..."
783 (- (length dabbrev--friend-buffer-list)) 0 0 1 1.5))))
513e7954
RS
784 ;; Walk through the buffers till we find a match.
785 (let (expansion)
786 (while (and (not expansion) dabbrev--friend-buffer-list)
2e27ed13 787 (setq dabbrev--last-buffer (pop dabbrev--friend-buffer-list))
513e7954 788 (set-buffer dabbrev--last-buffer)
7daa3523
TTN
789 (progress-reporter-update dabbrev--progress-reporter
790 (- (length dabbrev--friend-buffer-list)))
513e7954
RS
791 (setq dabbrev--last-expansion-location (point-min))
792 (setq expansion (dabbrev--try-find abbrev nil 1 ignore-case)))
793 expansion)))))
794
795;; Compute the list of buffers to scan.
796;; If dabbrev-search-these-buffers-only, then the current buffer
797;; is included in this list if it should be searched.
798;; Otherwise, the current buffer is searched first specially.,
799;; and it is not included in this list.
800(defun dabbrev--make-friend-buffer-list ()
801 (let ((list (mapcar (function get-buffer)
802 dabbrev-search-these-buffers-only)))
803 (when (and (null dabbrev-search-these-buffers-only)
804 dabbrev--check-other-buffers
805 (or (eq dabbrev--check-other-buffers t)
806 (setq dabbrev--check-other-buffers
807 (y-or-n-p "Scan other buffers also? "))))
808 (setq list (funcall dabbrev-select-buffers-function))
809 ;; If dabbrev-check-all-buffers, tack on all the other
810 ;; buffers at the end of the list, except those which are
811 ;; specifically to be ignored.
812 (if dabbrev-check-all-buffers
813 (setq list
814 (append list
3ffa545b
GM
815 (dabbrev-filter-elements
816 buffer (buffer-list)
513e7954
RS
817 (and (not (memq buffer list))
818 (not (dabbrev--ignore-buffer-p buffer)))))))
819 ;; Remove the current buffer.
820 (setq list (delq (current-buffer) list)))
821 ;; Move buffers in the list that are visible on the screen
822 ;; to the front of the list, but don't add anything to the list.
823 (if list
824 (walk-windows (lambda (w)
825 (unless (eq w (selected-window))
826 (if (memq (window-buffer w) list)
827 (setq list
828 (cons (window-buffer w)
829 (delq (window-buffer w)
830 list))))))))
831 ;; In a minibuffer, search the buffer it was activated from,
832 ;; first after the minibuffer itself. Unless we aren't supposed
833 ;; to search the current buffer either.
834 (if (and (window-minibuffer-p (selected-window))
835 (not dabbrev-search-these-buffers-only))
836 (setq list
837 (cons (dabbrev--minibuffer-origin)
838 (delq (dabbrev--minibuffer-origin) list))))
839 list))
6163b3ba 840
6163b3ba
RS
841(defun dabbrev--safe-replace-match (string &optional fixedcase literal)
842 (if (eq major-mode 'picture-mode)
6ee55cb1
RS
843 (with-no-warnings
844 (picture-replace-match string fixedcase literal))
6163b3ba
RS
845 (replace-match string fixedcase literal)))
846
847;;;----------------------------------------------------------------
3ffa545b
GM
848(defun dabbrev--substitute-expansion (old abbrev expansion record-case-pattern)
849 "Replace OLD with EXPANSION in the buffer.
850OLD is text currently in the buffer, perhaps the abbreviation
851or perhaps another expansion that was tried previously.
852ABBREV is the abbreviation we are expanding.
853It is \" \" if we are copying subsequent words.
854EXPANSION is the expansion substring to be used this time.
855RECORD-CASE-PATTERN, if non-nil, means set `dabbrev--last-case-pattern'
856to record whether we upcased the expansion, downcased it, or did neither."
6163b3ba 857 ;;(undo-boundary)
543abb4a
RS
858 (let ((use-case-replace (and (if (eq dabbrev-case-fold-search 'case-fold-search)
859 case-fold-search
860 dabbrev-case-fold-search)
6163b3ba
RS
861 (or (not dabbrev-upcase-means-case-search)
862 (string= abbrev (downcase abbrev)))
543abb4a
RS
863 (if (eq dabbrev-case-replace 'case-replace)
864 case-replace
865 dabbrev-case-replace))))
3ffa545b
GM
866
867 ;; If we upcased or downcased the original expansion,
868 ;; do likewise for the subsequent words when we copy them.
fd2dfb40
RS
869 ;; Don't do any of the usual case processing, though.
870 (when (equal abbrev " ")
871 (if dabbrev--last-case-pattern
872 (setq expansion
873 (funcall dabbrev--last-case-pattern expansion)))
874 (setq use-case-replace nil))
3ffa545b 875
65411629
RS
876 ;; If the expansion has mixed case
877 ;; and it is not simply a capitalized word,
878 ;; or if the abbrev has mixed case,
879 ;; and if the given abbrev's case pattern
66db4b5b
RS
880 ;; matches the start of the expansion,
881 ;; copy the expansion's case
882 ;; instead of downcasing all the rest.
e921af9e
RS
883 ;;
884 ;; Treat a one-capital-letter (possibly with preceding non-letter
885 ;; characters) abbrev as "not all upper case", so as to force
886 ;; preservation of the expansion's pattern if the expansion starts
887 ;; with a capital letter.
888 (let ((expansion-rest (substring expansion 1))
889 (first-letter-position (string-match "[[:alpha:]]" abbrev)))
890 (if (or (null first-letter-position)
891 (and (not (and (or (string= expansion-rest (downcase expansion-rest))
892 (string= expansion-rest (upcase expansion-rest)))
893 (or (string= abbrev (downcase abbrev))
894 (and (string= abbrev (upcase abbrev))
895 (> (- (length abbrev) first-letter-position)
896 1)))))
897 (string= abbrev
898 (substring expansion 0 (length abbrev)))))
65411629 899 (setq use-case-replace nil)))
fd2dfb40
RS
900
901 ;; If the abbrev and the expansion are both all-lower-case
902 ;; then don't do any conversion. The conversion would be a no-op
903 ;; for this replacement, but it would carry forward to subsequent words.
e921af9e 904 ;; The goal of this is to prevent that carrying forward.
fd2dfb40
RS
905 (if (and (string= expansion (downcase expansion))
906 (string= abbrev (downcase abbrev)))
07e47d0b 907 (setq use-case-replace nil))
fd2dfb40 908
07e47d0b
RS
909 (if use-case-replace
910 (setq expansion (downcase expansion)))
3ffa545b
GM
911
912 ;; In case we insert subsequent words,
913 ;; record if we upcased or downcased the first word,
914 ;; in order to do likewise for subsequent words.
915 (and record-case-pattern
71296446 916 (setq dabbrev--last-case-pattern
3ffa545b
GM
917 (and use-case-replace
918 (cond ((equal abbrev (upcase abbrev)) 'upcase)
919 ((equal abbrev (downcase abbrev)) 'downcase)))))
920
19c17c4e 921 ;; Convert whitespace to single spaces.
e2e75068
RS
922 (if dabbrev-eliminate-newlines
923 (let ((pos
924 (if (equal abbrev " ") 0 (length abbrev))))
925 ;; If ABBREV is real, search after the end of it.
926 ;; If ABBREV is space and we are copying successive words,
927 ;; search starting at the front.
19c17c4e
RS
928 (while (string-match "[\n \t]+" expansion pos)
929 (setq pos (1+ (match-beginning 0)))
930 (setq expansion (replace-match " " nil nil expansion)))))
fd2dfb40 931
6163b3ba
RS
932 (if old
933 (save-excursion
934 (search-backward old))
9b7c13e0 935 ;;(set-match-data (list (point-marker) (point-marker)))
fd2dfb40
RS
936 (search-backward abbrev)
937 (search-forward abbrev))
938
6163b3ba
RS
939 ;; Make case of replacement conform to case of abbreviation
940 ;; provided (1) that kind of thing is enabled in this buffer
941 ;; and (2) the replacement itself is all lower case.
942 (dabbrev--safe-replace-match expansion
943 (not use-case-replace)
944 t)))
945
946
947;;;----------------------------------------------------------------
948;;; Search function used by dabbrevs library.
949
6163b3ba
RS
950
951(defun dabbrev--search (abbrev reverse ignore-case)
fd2dfb40
RS
952 "Search for something that could be used to expand ABBREV.
953
954Second arg, REVERSE, is t for reverse search, nil for forward.
955The variable `dabbrev-limit' controls the maximum search region size.
956Third argument IGNORE-CASE non-nil means treat case as insignificant while
957looking for a match and when comparing with previous matches. Also if
958that's non-nil and the match is found at the beginning of a sentence
959and is in lower case except for the initial then it is converted to
960all lower case for return.
961
962Table of expansions already seen is examined in buffer
963`dabbrev--last-table' so that only distinct possibilities are found
964by dabbrev-re-expand.
965
966Returns the expansion found, or nil if not found.
967Leaves point at the location of the start of the expansion."
6163b3ba
RS
968 (save-match-data
969 (let ((pattern1 (concat (regexp-quote abbrev)
970 "\\(" dabbrev--abbrev-char-regexp "\\)"))
971 (pattern2 (concat (regexp-quote abbrev)
972 "\\(\\(" dabbrev--abbrev-char-regexp "\\)+\\)"))
155133a2
RS
973 ;; This makes it possible to find matches in minibuffer prompts
974 ;; even when they are "inviolable".
975 (inhibit-point-motion-hooks t)
93a43334 976 found-string result)
6163b3ba
RS
977 ;; Limited search.
978 (save-restriction
979 (and dabbrev-limit
980 (narrow-to-region dabbrev--last-expansion-location
981 (+ (point)
982 (if reverse (- dabbrev-limit) dabbrev-limit))))
983 ;;--------------------------------
984 ;; Look for a distinct expansion, using dabbrev--last-table.
985 ;;--------------------------------
986 (while (and (not found-string)
987 (if reverse
988 (re-search-backward pattern1 nil t)
989 (re-search-forward pattern1 nil t)))
63d991d6
KH
990 (goto-char (match-beginning 0))
991 ;; In case we matched in the middle of a word,
992 ;; back up to start of word and verify we still match.
993 (dabbrev--goto-start-of-abbrev)
994
995 (if (not (looking-at pattern1))
996 nil
997 ;; We have a truly valid match. Find the end.
6163b3ba 998 (re-search-forward pattern2)
2e27ed13 999 (setq found-string (match-string-no-properties 0))
93a43334 1000 (setq result found-string)
6163b3ba 1001 (and ignore-case (setq found-string (downcase found-string)))
63d991d6 1002 ;; Ignore this match if it's already in the table.
7313ccdb
RS
1003 (if (dabbrev-filter-elements
1004 table-string dabbrev--last-table
1005 (string= found-string table-string))
63d991d6
KH
1006 (setq found-string nil)))
1007 ;; Prepare to continue searching.
2e27ed13 1008 (goto-char (if reverse (match-beginning 0) (match-end 0))))
63d991d6 1009 ;; If we found something, use it.
93a43334
RS
1010 (when found-string
1011 ;; Put it into `dabbrev--last-table'
1012 ;; and return it (either downcased, or as is).
1013 (setq dabbrev--last-table
1014 (cons found-string dabbrev--last-table))
1015 result)))))
6163b3ba 1016
df27f31f 1017(dolist (mess '("^No dynamic expansion for .* found"
87207d14
DL
1018 "^No further dynamic expansion for .* found$"
1019 "^No possible abbreviation preceding point$"))
1020 (add-to-list 'debug-ignored-errors mess))
1021
01987a6b 1022(provide 'dabbrev)
7313ccdb 1023
cbee283d 1024;; arch-tag: 29e58596-f080-4306-a409-70296cf9d46f
b578f267 1025;;; dabbrev.el ends here