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