Switch to recommended form of GPLv3 permissions notice.
[bpt/emacs.git] / lisp / dabbrev.el
1 ;;; dabbrev.el --- dynamic abbreviation package
2
3 ;; Copyright (C) 1985, 1986, 1992, 1994, 1996, 1997, 2000, 2001, 2002,
4 ;; 2003, 2004, 2005, 2006, 2007, 2008 Free Software Foundation, Inc.
5
6 ;; Author: Don Morrison
7 ;; Maintainer: Lars Lindberg <Lars.Lindberg@sypro.cap.se>
8 ;; Created: 16 Mars 1992
9 ;; Lindberg's last update version: 5.7
10 ;; Keywords: abbrev expand completion convenience
11
12 ;; This file is part of GNU Emacs.
13
14 ;; GNU Emacs is free software: you can redistribute it and/or modify
15 ;; it under the terms of the GNU General Public License as published by
16 ;; the Free Software Foundation, either version 3 of the License, or
17 ;; (at your option) any later version.
18
19 ;; GNU Emacs is distributed in the hope that it will be useful,
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.
23
24 ;; You should have received a copy of the GNU General Public License
25 ;; along with GNU Emacs. If not, see <http://www.gnu.org/licenses/>.
26
27 ;;; Commentary:
28
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 ;;
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)
47 ;; Then you don't interfere with other modes.
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
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.
77 ;; - Check the kill-ring when everything else fails. (Maybe something
78 ;; for hippie-expand?). [Bng] <boris@cs.rochester.edu>
79
80 ;;; These people gave suggestions:
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>
90 ;; [ejb] Jay Berkenbilt <ejb@ql.org>
91 ;; [hawley] Bob Hawley <rth1@quartet.mt.att.com>
92 ;; ... and to all the people who have participated in the beta tests.
93
94 ;;; Code:
95
96 ;;----------------------------------------------------------------
97 ;; Customization variables
98 ;;----------------------------------------------------------------
99
100 (defgroup dabbrev nil
101 "Dynamic Abbreviations."
102 :tag "Dynamic Abbreviations"
103 :group 'abbrev
104 :group 'convenience)
105
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
118 "*Regexp for skipping leading characters of an abbreviation.
119
120 Example: Set this to \"\\\\$\" for programming languages
121 in which variable names may appear with or without a leading `$'.
122 \(For example, in Makefiles.\)
123
124 Set this to nil if no characters should be skipped."
125 :type '(choice regexp
126 (const :tag "off" nil))
127 :group 'dabbrev)
128
129 (defcustom dabbrev-eliminate-newlines t
130 "*Non-nil means dabbrev should not insert newlines.
131 Instead it converts them to spaces."
132 :type 'boolean
133 :group 'dabbrev)
134
135 (defcustom dabbrev-case-fold-search 'case-fold-search
136 "*Control whether dabbrev searches should ignore case.
137 A value of nil means case is significant.
138 A value of `case-fold-search' means case is significant
139 if `case-fold-search' is nil.
140 Any other non-nil version means case is not significant."
141 :type '(choice (const :tag "off" nil)
142 (const :tag "like search" case-fold-search)
143 (other :tag "on" t))
144 :group 'dabbrev)
145
146 (defcustom dabbrev-upcase-means-case-search nil
147 "*The significance of an uppercase character in an abbreviation.
148 A nil value means case fold search when searching for possible expansions;
149 non-nil means case sensitive search.
150
151 This variable has an effect only when the value of
152 `dabbrev-case-fold-search' says to ignore case."
153 :type 'boolean
154 :group 'dabbrev)
155
156 (defcustom dabbrev-case-distinction 'case-replace
157 "*Whether dabbrev treats expansions as the same if they differ in case.
158
159 A value of nil means treat them as different.
160 A value of `case-replace' means distinguish them if `case-replace' is nil.
161 Any other non-nil value means to treat them as the same.
162
163 This 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
169 :version "22.1")
170
171 (defcustom dabbrev-case-replace 'case-replace
172 "*Whether dabbrev applies the abbreviations's case pattern to the expansion.
173
174 A value of nil means preserve the expansion's case pattern.
175 A value of `case-replace' means preserve it if `case-replace' is nil.
176 Any other non-nil value means modify the expansion
177 by applying the abbreviation's case pattern to it.
178
179 This variable has an effect only when the value of
180 `dabbrev-case-fold-search' specifies to ignore case."
181 :type '(choice (const :tag "off" nil)
182 (const :tag "based on `case-replace'" case-replace)
183 (other :tag "on" t))
184 :group 'dabbrev)
185
186 (defcustom dabbrev-abbrev-char-regexp nil
187 "*Regexp to recognize a character in an abbreviation or expansion.
188 This regexp will be surrounded with \\\\( ... \\\\) when actually used.
189
190 Set this variable to \"\\\\sw\" if you want ordinary words or
191 \"\\\\sw\\\\|\\\\s_\" if you want symbols (including characters whose
192 syntax is \"symbol\" as well as those whose syntax is \"word\".
193
194 The value nil has a special meaning: the abbreviation is from point to
195 previous word-start, but the search is for symbols.
196
197 For instance, if you are programming in Lisp, `yes-or-no-p' is a symbol,
198 while `yes', `or', `no' and `p' are considered words. If this
199 variable is nil, then expanding `yes-or-no-' looks for a symbol
200 starting 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
203 expanding `yes-or-no-' signals an error because `-' is not part of a word;
204 but expanding `yes-or-no' looks for a word starting with `no'.
205
206 The recommended value is \"\\\\sw\\\\|\\\\s_\"."
207 :type '(choice (const nil)
208 regexp)
209 :group 'dabbrev)
210
211 (defcustom dabbrev-check-all-buffers t
212 "*Non-nil means dabbrev package should search *all* buffers.
213
214 Dabbrev always searches the current buffer first. Then, if
215 `dabbrev-check-other-buffers' says so, it searches the buffers
216 designated by `dabbrev-select-buffers-function'.
217
218 Then, if `dabbrev-check-all-buffers' is non-nil, dabbrev searches
219 all the other buffers, except those named in `dabbrev-ignored-buffer-names',
220 or matched by `dabbrev-ignored-regexps'."
221 :type 'boolean
222 :group 'dabbrev)
223
224 (defcustom dabbrev-ignored-buffer-names '("*Messages*" "*Buffer List*")
225 "*List of buffer names that dabbrev should not check.
226 See also `dabbrev-ignored-buffer-regexps'."
227 :type '(repeat (string :tag "Buffer name"))
228 :group 'dabbrev
229 :version "20.3")
230
231 (defcustom dabbrev-ignored-buffer-regexps nil
232 "*List of regexps matching names of buffers that dabbrev should not check.
233 See also `dabbrev-ignored-buffer-names'."
234 :type '(repeat regexp)
235 :group 'dabbrev
236 :version "21.1")
237
238 (defcustom dabbrev-check-other-buffers t
239 "*Should \\[dabbrev-expand] look in other buffers?\
240
241 nil: Don't look in other buffers.
242 t: Also look for expansions in the buffers pointed out by
243 `dabbrev-select-buffers-function'.
244 Anything else: When we can't find any more expansions in
245 the current buffer, then ask the user whether to look in other
246 buffers too.
247
248 The default value is t."
249 :type '(choice (const :tag "off" nil)
250 (const :tag "on" t)
251 (other :tag "ask" other))
252 :group 'dabbrev)
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.
259 The function should take no arguments and return a list of buffers to
260 search for expansions. See the source of `dabbrev--select-buffers'
261 for an example.
262
263 A mode setting this variable should make it buffer local.")
264
265 (defcustom dabbrev-friend-buffer-function 'dabbrev--same-major-mode-p
266 "*A function to decide whether dabbrev should search OTHER-BUFFER.
267 The function should take one argument, OTHER-BUFFER, and return
268 non-nil if that buffer should be searched. Have a look at
269 `dabbrev--same-major-mode-p' for an example.
270
271 The value of `dabbrev-friend-buffer-function' has an effect only if
272 the value of `dabbrev-select-buffers-function' uses it. The function
273 `dabbrev--select-buffers' is one function you can use here.
274
275 A mode setting this variable should make it buffer local."
276 :type 'function
277 :group 'dabbrev)
278
279 (defcustom dabbrev-search-these-buffers-only nil
280 "If non-nil, a list of buffers which dabbrev should search.
281 If this variable is non-nil, dabbrev will only look in these buffers.
282 It will not even look in the current buffer if it is not a member of
283 this list."
284 :group 'dabbrev)
285
286 ;;----------------------------------------------------------------
287 ;; Internal variables
288 ;;----------------------------------------------------------------
289
290 ;; Last obarray of completions in `dabbrev-completion'
291 (defvar dabbrev--last-obarray nil)
292
293 ;; Table of expansions seen so far
294 (defvar dabbrev--last-table nil)
295
296 ;; Last string we tried to expand.
297 (defvar dabbrev--last-abbreviation nil)
298
299 ;; Location last abbreviation began
300 (defvar dabbrev--last-abbrev-location nil)
301
302 ;; Direction of last dabbrevs search
303 (defvar dabbrev--last-direction 0)
304
305 ;; Last expansion of an abbreviation.
306 (defvar dabbrev--last-expansion nil)
307
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
314 ;; The buffer we looked in last, not counting the current buffer.
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
323 ;; If non-nil, a function to use when copying successive words.
324 ;; It should be `upcase' or `downcase'.
325 (defvar dabbrev--last-case-pattern nil)
326
327 ;; Same as dabbrev-check-other-buffers, but is set for every expand.
328 (defvar dabbrev--check-other-buffers dabbrev-check-other-buffers)
329
330 ;; The regexp for recognizing a character in an abbreviation.
331 (defvar dabbrev--abbrev-char-regexp nil)
332
333 ;; The progress reporter for buffer-scanning progress.
334 (defvar dabbrev--progress-reporter nil)
335
336 ;;----------------------------------------------------------------
337 ;; Macros
338 ;;----------------------------------------------------------------
339
340 ;;; Get the buffer that mini-buffer was activated from
341 (defsubst dabbrev--minibuffer-origin ()
342 (car (cdr (buffer-list))))
343
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)
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)))
357
358 ;;----------------------------------------------------------------
359 ;; Exported functions
360 ;;----------------------------------------------------------------
361
362 ;;;###autoload (define-key esc-map "/" 'dabbrev-expand)
363 ;;;??? Do we want this?
364 ;;;###autoload (define-key esc-map [?\C-/] 'dabbrev-completion)
365
366 ;;;###autoload
367 (defun dabbrev-completion (&optional arg)
368 "Completion on current word.
369 Like \\[dabbrev-expand] but finds all expansions in the current buffer
370 and presents suggestions for completion.
371
372 With a prefix argument, it searches all buffers accepted by the
373 function pointed out by `dabbrev-friend-buffer-function' to find the
374 completions.
375
376 If the prefix argument is 16 (which comes from C-u C-u),
377 then it searches *all* buffers."
378 (interactive "*P")
379 (dabbrev--reset-global-variables)
380 (let* ((dabbrev-check-other-buffers (and arg t))
381 (dabbrev-check-all-buffers
382 (and arg (= (prefix-numeric-value arg) 16)))
383 (abbrev (dabbrev--abbrev-at-point))
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)))))
389 (my-obarray dabbrev--last-obarray)
390 init)
391 (save-excursion
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))))
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)
439 (message "Repeat `%s' to see all completions"
440 (key-description (this-command-keys)))
441 (message "The only possible completion"))
442 (dabbrev--substitute-expansion nil abbrev init nil))
443 (t
444 ;; * String is a common substring completion already. Make list.
445 (message "Making completion list...")
446 (with-output-to-temp-buffer "*Completions*"
447 (display-completion-list (all-completions init my-obarray)
448 init))
449 (message "Making completion list...done")))
450 (and (window-minibuffer-p (selected-window))
451 (message nil))))
452
453 ;;;###autoload
454 (defun dabbrev-expand (arg)
455 "Expand previous word \"dynamically\".
456
457 Expands to the most recent, preceding word for which this is a prefix.
458 If no suitable preceding word is found, words following point are
459 considered. If still no suitable word is found, then look in the
460 buffers accepted by the function pointed out by variable
461 `dabbrev-friend-buffer-function'.
462
463 A positive prefix argument, N, says to take the Nth backward *distinct*
464 possibility. A negative argument says search forward.
465
466 If the cursor has not moved from the end of the previous expansion and
467 no argument is given, replace the previously-made expansion
468 with the next possible expansion not yet tried.
469
470 The variable `dabbrev-backward-only' may be used to limit the
471 direction of search to backward if set non-nil.
472
473 See also `dabbrev-abbrev-char-regexp' and \\[dabbrev-completion]."
474 (interactive "*P")
475 (let (abbrev record-case-pattern
476 expansion old direction (orig-point (point)))
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)
481 (save-excursion
482 (if (and (null arg)
483 (markerp dabbrev--last-abbrev-location)
484 (marker-position dabbrev--last-abbrev-location)
485 (or (eq last-command this-command)
486 (and (window-minibuffer-p (selected-window))
487 (= dabbrev--last-abbrev-location
488 (point)))))
489 ;; Find a different expansion for the same abbrev as last time.
490 (progn
491 (setq abbrev dabbrev--last-abbreviation)
492 (setq old dabbrev--last-expansion)
493 (setq direction dabbrev--last-direction))
494 ;; If the user inserts a space after expanding
495 ;; and then asks to expand again, always fetch the next word.
496 (if (and (eq (preceding-char) ?\s)
497 (markerp dabbrev--last-abbrev-location)
498 (marker-position dabbrev--last-abbrev-location)
499 (= (point) (1+ dabbrev--last-abbrev-location)))
500 (progn
501 ;; The "abbrev" to expand is just the space.
502 (setq abbrev " ")
503 (save-excursion
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))))
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))
535 (setq record-case-pattern t)
536 (setq old nil)))
537
538 ;;--------------------------------
539 ;; Find the expansion
540 ;;--------------------------------
541 (or expansion
542 (setq expansion
543 (dabbrev--find-expansion abbrev direction
544 (and (if (eq dabbrev-case-fold-search 'case-fold-search)
545 case-fold-search
546 dabbrev-case-fold-search)
547 (or (not dabbrev-upcase-means-case-search)
548 (string= abbrev (downcase abbrev))))))))
549 (cond
550 ((not expansion)
551 (dabbrev--reset-global-variables)
552 (if old
553 (save-excursion
554 (setq buffer-undo-list (cons orig-point buffer-undo-list))
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)))))
559 (error "No%s dynamic expansion for `%s' found"
560 (if old " further" "") abbrev))
561 (t
562 (if (not (or (eq dabbrev--last-buffer dabbrev--last-buffer-found)
563 (minibuffer-window-active-p (selected-window))))
564 (progn
565 (message "Expansion found in '%s'"
566 (buffer-name dabbrev--last-buffer))
567 (setq dabbrev--last-buffer-found dabbrev--last-buffer))
568 (message nil))
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)))
575 ;; Success: stick it in and return.
576 (setq buffer-undo-list (cons orig-point buffer-undo-list))
577 (dabbrev--substitute-expansion old abbrev expansion
578 record-case-pattern)
579
580 ;; Save state for re-expand.
581 (setq dabbrev--last-expansion expansion)
582 (setq dabbrev--last-abbreviation abbrev)
583 (setq dabbrev--last-abbrev-location (point-marker))))))
584
585 ;;----------------------------------------------------------------
586 ;; Local functions
587 ;;----------------------------------------------------------------
588
589 ;;; Checks if OTHER-BUFFER has the same major mode as current buffer.
590 (defun dabbrev--same-major-mode-p (other-buffer)
591 (eq major-mode
592 (save-excursion
593 (set-buffer other-buffer)
594 major-mode)))
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
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)))
610 (and dabbrev-abbrev-skip-leading-regexp
611 (while (looking-at dabbrev-abbrev-skip-leading-regexp)
612 (forward-char 1)))))
613
614 ;;; Extract the symbol at point to serve as abbreviation.
615 (defun dabbrev--abbrev-at-point ()
616 ;; Check for error
617 (if (bobp)
618 (error "No possible abbreviation preceding point"))
619 ;; Return abbrev at point
620 (save-excursion
621 ;; Record the end of the abbreviation.
622 (setq dabbrev--last-abbrev-location (point))
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)
641 (buffer-substring-no-properties
642 dabbrev--last-abbrev-location (point))))
643
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_")
659 dabbrev--check-other-buffers dabbrev-check-other-buffers))
660
661 (defun dabbrev--select-buffers ()
662 "Return a list of other buffers to search for a possible abbrev.
663 The current buffer is not included in the list.
664
665 This function makes a list of all the buffers returned by `buffer-list',
666 then discards buffers whose names match `dabbrev-ignored-buffer-names'
667 or `dabbrev-ignored-buffer-regexps'. It also discards buffers for which
668 `dabbrev-friend-buffer-function', if it is bound, returns nil when called
669 with the buffer as argument.
670 It 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)
676 (funcall dabbrev-friend-buffer-function buffer))))
677
678 (defun dabbrev--try-find (abbrev reverse n ignore-case)
679 "Search for ABBREV, backwards if REVERSE, N times.
680 If IGNORE-CASE is non-nil, ignore case while searching.
681 Return the expansion found, and save the location of the start
682 of the expansion in `dabbrev--last-expansion-location'."
683 (save-excursion
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
694 (and ignore-case
695 (if (eq dabbrev-case-distinction 'case-replace)
696 case-replace
697 dabbrev-case-distinction))
698 )))
699 (setq count (1- count))))
700 (and expansion
701 (setq dabbrev--last-expansion-location (point)))
702 expansion))))
703
704 (defun dabbrev--find-all-expansions (abbrev ignore-case)
705 "Return a list of all possible expansions of ABBREV.
706 If IGNORE-CASE is non-nil, accept matches which differ in case."
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))
712 (setq all-expansions (cons expansion all-expansions))))
713 all-expansions))
714
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
726 (defun dabbrev--find-expansion (abbrev direction ignore-case)
727 "Find one occurrence of ABBREV, and return the expansion.
728 DIRECTION > 0 means look that many times backwards.
729 DIRECTION < 0 means look that many times forward.
730 DIRECTION = 0 means try both backward and forward.
731 IGNORE-CASE non-nil means ignore case when searching.
732 This sets `dabbrev--last-direction' to 1 or -1 according
733 to the direction in which the occurrence was actually found.
734 It sets `dabbrev--last-expansion-location' to the location
735 of the start of the occurrence."
736 (save-excursion
737 ;; If we were scanning something other than the current buffer,
738 ;; continue scanning there.
739 (when dabbrev--last-buffer
740 (set-buffer dabbrev--last-buffer))
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.
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))))
779 ;; Walk through the buffers till we find a match.
780 (let (expansion)
781 (while (and (not expansion) dabbrev--friend-buffer-list)
782 (setq dabbrev--last-buffer (pop dabbrev--friend-buffer-list))
783 (set-buffer dabbrev--last-buffer)
784 (progress-reporter-update dabbrev--progress-reporter
785 (- (length dabbrev--friend-buffer-list)))
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
810 (dabbrev-filter-elements
811 buffer (buffer-list)
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))
835
836 (defun dabbrev--safe-replace-match (string &optional fixedcase literal)
837 (if (eq major-mode 'picture-mode)
838 (with-no-warnings
839 (picture-replace-match string fixedcase literal))
840 (replace-match string fixedcase literal)))
841
842 ;;;----------------------------------------------------------------
843 (defun dabbrev--substitute-expansion (old abbrev expansion record-case-pattern)
844 "Replace OLD with EXPANSION in the buffer.
845 OLD is text currently in the buffer, perhaps the abbreviation
846 or perhaps another expansion that was tried previously.
847 ABBREV is the abbreviation we are expanding.
848 It is \" \" if we are copying subsequent words.
849 EXPANSION is the expansion substring to be used this time.
850 RECORD-CASE-PATTERN, if non-nil, means set `dabbrev--last-case-pattern'
851 to record whether we upcased the expansion, downcased it, or did neither."
852 ;;(undo-boundary)
853 (let ((use-case-replace (and (if (eq dabbrev-case-fold-search 'case-fold-search)
854 case-fold-search
855 dabbrev-case-fold-search)
856 (or (not dabbrev-upcase-means-case-search)
857 (string= abbrev (downcase abbrev)))
858 (if (eq dabbrev-case-replace 'case-replace)
859 case-replace
860 dabbrev-case-replace))))
861
862 ;; If we upcased or downcased the original expansion,
863 ;; do likewise for the subsequent words when we copy them.
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))
870
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
875 ;; matches the start of the expansion,
876 ;; copy the expansion's case
877 ;; instead of downcasing all the rest.
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)))))
894 (setq use-case-replace nil)))
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.
899 ;; The goal of this is to prevent that carrying forward.
900 (if (and (string= expansion (downcase expansion))
901 (string= abbrev (downcase abbrev)))
902 (setq use-case-replace nil))
903
904 (if use-case-replace
905 (setq expansion (downcase expansion)))
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
911 (setq dabbrev--last-case-pattern
912 (and use-case-replace
913 (cond ((equal abbrev (upcase abbrev)) 'upcase)
914 ((equal abbrev (downcase abbrev)) 'downcase)))))
915
916 ;; Convert whitespace to single spaces.
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.
923 (while (string-match "[\n \t]+" expansion pos)
924 (setq pos (1+ (match-beginning 0)))
925 (setq expansion (replace-match " " nil nil expansion)))))
926
927 (if old
928 (save-excursion
929 (search-backward old))
930 ;;(set-match-data (list (point-marker) (point-marker)))
931 (search-backward abbrev)
932 (search-forward abbrev))
933
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
945
946 (defun dabbrev--search (abbrev reverse ignore-case)
947 "Search for something that could be used to expand ABBREV.
948
949 Second arg, REVERSE, is t for reverse search, nil for forward.
950 The variable `dabbrev-limit' controls the maximum search region size.
951 Third argument IGNORE-CASE non-nil means treat case as insignificant while
952 looking for a match and when comparing with previous matches. Also if
953 that's non-nil and the match is found at the beginning of a sentence
954 and is in lower case except for the initial then it is converted to
955 all lower case for return.
956
957 Table of expansions already seen is examined in buffer
958 `dabbrev--last-table' so that only distinct possibilities are found
959 by dabbrev-re-expand.
960
961 Returns the expansion found, or nil if not found.
962 Leaves point at the location of the start of the expansion."
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 "\\)+\\)"))
968 ;; This makes it possible to find matches in minibuffer prompts
969 ;; even when they are "inviolable".
970 (inhibit-point-motion-hooks t)
971 found-string result)
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)))
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.
993 (re-search-forward pattern2)
994 (setq found-string (match-string-no-properties 0))
995 (setq result found-string)
996 (and ignore-case (setq found-string (downcase found-string)))
997 ;; Ignore this match if it's already in the table.
998 (if (dabbrev-filter-elements
999 table-string dabbrev--last-table
1000 (string= found-string table-string))
1001 (setq found-string nil)))
1002 ;; Prepare to continue searching.
1003 (goto-char (if reverse (match-beginning 0) (match-end 0))))
1004 ;; If we found something, use it.
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)))))
1011
1012 (dolist (mess '("^No dynamic expansion for .* found"
1013 "^No further dynamic expansion for .* found$"
1014 "^No possible abbreviation preceding point$"))
1015 (add-to-list 'debug-ignored-errors mess))
1016
1017 (provide 'dabbrev)
1018
1019 ;; arch-tag: 29e58596-f080-4306-a409-70296cf9d46f
1020 ;;; dabbrev.el ends here