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