(DEFVAR_DISPLAY): New macro.
[emacs.git] / lisp / dabbrev.el
blob430824de8ebbcf1bb151c7dcc3f0cc4ac84130ce
1 ;;; dabbrev.el --- dynamic abbreviation package
2 ;; Copyright (C) 1985, 1986, 1992, 1994 Free Software Foundation, Inc.
4 ;; Author: Don Morrison
5 ;; Maintainer: Lars Lindberg <Lars.Lindberg@sypro.cap.se>
6 ;; Created: 16 Mars 1992
7 ;; Lindberg's last update version: 5.2
8 ;; Keywords: abbrev expand completion
10 ;; This program is free software; you can redistribute it and/or modify
11 ;; it under the terms of the GNU General Public License as published by
12 ;; the Free Software Foundation; either version 2 of the License, or
13 ;; (at your option) any later version.
15 ;; This program is distributed in the hope that it will be useful,
16 ;; but WITHOUT ANY WARRANTY; without even the implied warranty of
17 ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18 ;; GNU General Public License for more details.
20 ;; You should have received a copy of the GNU General Public License
21 ;; along with this program; if not, write to the Free Software
22 ;; Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
24 ;;; Commentary:
26 ;; The purpose with this package is to let you write just a few
27 ;; characters of words you've written earlier to be able to expand
28 ;; them.
30 ;; To expand a word, just put the point right after the word and press
31 ;; M-/ (dabbrev-expand) or M-C-/ (dabbrev-completion).
33 ;; There are powerful things in this package that aren't turned on by
34 ;; default. I recommend you to do the following.
36 ;; Put the following 2 lines in your .emacs file:
37 ;; (setq dabbrev-always-check-other-buffers t)
38 ;; (setq dabbrev-abbrev-char-regexp "\\sw\\|\\s_")
40 ;; Dabbrev will now search in all buffers with the same major mode for
41 ;; your expansions. It will also search for complete symbols, the old
42 ;; dabbrev package only looked half-heartedly for symbols.
44 ;; Check out the customizable variables below to learn about all the
45 ;; features of this package.
47 ;;; Hints and tips for major modes writers:
49 ;; Recommended values C/Lisp etc text
50 ;; dabbrev-case-fold-search nil t
51 ;; dabbrev-case-replace nil t
53 ;; Set the variables you want special for your mode like this:
54 ;; (set (make-local-variable 'dabbrev-case-replace) nil)
55 ;; Then you don't interfer with other modes.
57 ;; If your mode handles buffers that refers to other buffers
58 ;; (i.e. compilation-mode, gud-mode), then try to set
59 ;; `dabbrev-select-buffers-function' or `dabbrev-friend-buffer-function'
60 ;; to a function that point out those buffers.
62 ;; Same goes for major-modes that are connected to other modes. There
63 ;; are for instance a number of mail-modes. One for reading, one for
64 ;; creating a new mail etc. Maybe those should be connected.
66 ;; Example for GNUS (when we write a reply, we want dabbrev to look in
67 ;; the article for expansion):
68 ;; (set (make-local-variable 'dabbrev-friend-buffer-function)
69 ;; (lambda (buffer)
70 ;; (save-excursion
71 ;; (set-buffer buffer)
72 ;; (memq major-mode '(news-reply-mode gnus-article-mode)))))
75 ;; Known bugs and limitations.
76 ;; - Possible to do several levels of `dabbrev-completion' in the
77 ;; minibuffer.
78 ;; - dabbrev-completion doesn't handle resetting the globals variables
79 ;; right. It resets them after finding the abbrev.
81 ;; Future enhancements
82 ;; - Check the tags-files? Like tags-complete?
83 ;; - Add the possibility of searching both forward and backward to
84 ;; the nearest expansion.
85 ;; - Check the kill-ring when everything else fails. (Maybe something
86 ;; for hippie-expand?). [Bng] <boris@cs.rochester.edu>
88 ;;; Thanks goes to
89 ;; [hymie] Hyman Rosen <marks!hymie@jyacc.jyacc.com>
90 ;; [burgett] Steve Burgett <burgett@bizet.eecs.berkeley.edu>
91 ;; [jules] Julian Gosnell <jules@x.co.uk>
92 ;; [kifer] Michael Kifer <kifer@sbcs.sunysb.edu>
93 ;; [ake] Ake Stenhoff <extaksf@aom.ericsson.se>
94 ;; [alon] Alon Albert <al%imercury@uunet.uu.net>
95 ;; [tromey] Tom Tromey <tromey@busco.lanl.gov>
96 ;; [Rolf] Rolf Schreiber <rolf@mathematik.uni-stuttgart.de>
97 ;; [Petri] Petri Raitio <per@tekla.fi>
98 ;; [ejb] Jay Berkenbilt <ejb@ERA.COM>
99 ;; [hawley] Bob Hawley <rth1@quartet.mt.att.com>
100 ;; ... and to all the people who have participated in the beta tests.
102 ;;; Code:
104 ;;;----------------------------------------------------------------
105 ;;;----------------------------------------------------------------
106 ;;; Customization variables
107 ;;;----------------------------------------------------------------
108 ;;;----------------------------------------------------------------
109 (defvar dabbrev-backward-only nil
110 "*If non-nil, `dabbrev-expand' only looks backwards.")
112 (defvar dabbrev-limit nil
113 "*Limits region searched by `dabbrev-expand' to this many chars away.")
115 (defvar dabbrev-abbrev-skip-leading-regexp nil
116 "*Regexp for skipping leading characters of an abbreviation.
118 Example: Set this to \"\\\\$\" for programming languages
119 in which variable names may appear with or without a leading `$'.
120 (For example, in Makefiles.)
122 Set this to nil if no characters should be skipped.")
124 ;; I recommend that you set this to nil.
125 (defvar dabbrev-case-fold-search 'case-fold-search
126 "*Non-nil if dabbrev searches should ignore case.
127 A value of nil means case is significant.
129 The value of this variable is an expression; it is evaluated
130 and the resulting value determines the decision.
131 For example: setting this to `case-fold-search' means evaluate that
132 variable to see whether its value is nil.")
134 (defvar dabbrev-upcase-means-case-search nil
135 "*The significance of an uppercase character in an abbreviation.
136 nil means case fold search, non-nil means case sensitive search.
138 This variable has an effect only when the value of
139 `dabbrev-case-fold-search' evaluates to t.")
141 ;; I recommend that you set this to nil.
142 (defvar dabbrev-case-replace 'case-replace
143 "*Non-nil means dabbrev should preserve case when expanding the abbreviation.
144 The value of this variable is an expression; it is evaluated
145 and the resulting value determines the decision.
146 For example, setting this to `case-replace' means evaluate that
147 variable to see if its value is t or nil.
149 This variable has an effect only when the value of
150 `dabbrev-case-fold-search' evaluates to t.")
152 (defvar dabbrev-abbrev-char-regexp "\\sw\\|\\s_"
153 "*Regexp to recognize a character in an abbreviation or expansion.
154 This regexp will be surrounded with \\\\( ... \\\\) when actually used.
156 Set this variable to \"\\\\sw\" if you want ordinary words or
157 \"\\\\sw\\\\|\\\\s_\" if you want symbols.
159 You can also set it to nil if you want old-style dabbrev searching
160 \(the abbreviation is from point to previous word-start, the
161 search is for symbols).
163 For instance, if you are programming in Lisp, `yes-or-no-p' is a symbol,
164 while `yes', `or', `no' and `p' are considered words. If you set this
165 variable to nil, then expanding `yes-or-no-' looks for a symbol
166 starting with or containing `no-'. If you set this variable to
167 \"\\\\sw\\\\|\\\\s_\", that expansion looks for a symbol starting with
168 `yes-or-no-'. Finally, if you set this variable to \"\\\\sw\", then
169 expanding `yes-or-no-' signals an error because `-' is not part of a word;
170 but expanding `yes-or-no' looks for a word starting with `no'.
172 The recommended value is \"\\\\sw\\\\|\\\\s_\".")
174 (defvar dabbrev-check-rest-of-buffers t
175 "*Non-nil means dabbrev package should search in all buffers.
176 It searches the buffers pointed out by `dabbrev-select-buffers-function'
177 first; afterward it looks in the rest of the buffers.")
180 ;; I recommend that you set this to t.
181 (defvar dabbrev-always-check-other-buffers nil
182 "*Should \\[dabbrev-expand] look in other buffers?\
183 nil = Don't look in other buffers.\n\
184 t = Look in other buffers.\n\
185 Value other than nil and t = ask the user if he want's to look in
186 other buffers.
188 The recommended value is t.")
190 ;; I guess setting this to a function that selects all C- or C++-
191 ;; mode buffers would be a good choice for a debugging buffer,
192 ;; when debugging C- or C++-code.
193 (defvar dabbrev-select-buffers-function 'dabbrev--select-buffers
194 "A function that selects buffers that should be searched by dabbrev.
196 The function should take no arguments and return a list of buffers to
197 search for expansions. Have a look at `dabbrev--select-buffers' for
198 an example.
200 A mode setting this variable should make it buffer local.")
202 (defvar dabbrev-friend-buffer-function 'dabbrev--same-major-mode-p
203 "*A function to check if OTHER-BUFFER should be searched by dabbrev.
205 The function should take one argument, OTHER-BUFFER, and return
206 non-nil if that buffer should be searched. Have a look at
207 `dabbrev--same-major-mode-p' for an example.
209 The value of `dabbrev-friend-buffer-function' has an effect only if
210 the value of `dabbrev-select-buffers-function' uses it. The function
211 `dabbrev--select-buffers' is one function you can use here.
213 A mode setting this variable should make it buffer local.")
215 (defvar dabbrev-search-these-buffers-only nil
216 "If non-nil, a list of buffers which dabbrev should search.
217 If this variable is non-nil, dabbrev will only look in these buffers.
218 It will not even look in the current buffer if it is not a member of
219 this list.")
221 ;;;----------------------------------------------------------------
222 ;;;----------------------------------------------------------------
223 ;;; Internal variables
224 ;;;----------------------------------------------------------------
225 ;;;----------------------------------------------------------------
227 ;; Last obarray of completions in `dabbrev-completion'
228 (defvar dabbrev--last-obarray nil)
230 ;; Table of expansions seen so far
231 (defvar dabbrev--last-table nil)
233 ;; Last string we tried to expand.
234 (defvar dabbrev--last-abbreviation nil)
236 ;; Location last abbreviation began
237 (defvar dabbrev--last-abbrev-location nil)
239 ;; Direction of last dabbrevs search
240 (defvar dabbrev--last-direction 0)
242 ;; Last expansion of an abbreviation.
243 (defvar dabbrev--last-expansion nil)
245 ;; Location the last expansion was found.
246 (defvar dabbrev--last-expansion-location nil)
248 ;; The list of remaining buffers with the same mode as current buffer.
249 (defvar dabbrev--friend-buffer-list nil)
251 ;; The buffer we looked in last.
252 (defvar dabbrev--last-buffer nil)
254 ;; The buffer we found the expansion last time.
255 (defvar dabbrev--last-buffer-found nil)
257 ;; The buffer we last did a completion in.
258 (defvar dabbrev--last-completion-buffer nil)
260 ;; Same as dabbrev-always-check-other-buffers, but is set for every expand.
261 (defvar dabbrev--check-other-buffers dabbrev-always-check-other-buffers)
263 ;; The regexp for recognizing a character in an abbreviation.
264 (defvar dabbrev--abbrev-char-regexp nil)
266 ;;;----------------------------------------------------------------
267 ;;;----------------------------------------------------------------
268 ;;; Macros
269 ;;;----------------------------------------------------------------
270 ;;;----------------------------------------------------------------
272 ;;; Get the buffer that mini-buffer was activated from
273 (defsubst dabbrev--minibuffer-origin ()
274 (car (cdr (buffer-list))))
276 ;; Make a list of some of the elements of LIST.
277 ;; Check each element of LIST, storing it temporarily in the
278 ;; variable ELEMENT, and include it in the result
279 ;; if CONDITION evaluates non-nil.
280 (defmacro dabbrev-filter-elements (element list condition)
281 (` (let (dabbrev-result dabbrev-tail (, element))
282 (setq dabbrev-tail (, list))
283 (while dabbrev-tail
284 (setq (, element) (car dabbrev-tail))
285 (if (, condition)
286 (setq dabbrev-result (cons (, element) dabbrev-result)))
287 (setq dabbrev-tail (cdr dabbrev-tail)))
288 (nreverse dabbrev-result))))
290 ;;;----------------------------------------------------------------
291 ;;;----------------------------------------------------------------
292 ;;; Exported functions
293 ;;;----------------------------------------------------------------
294 ;;;----------------------------------------------------------------
296 ;;;###autoload
297 (define-key esc-map "/" 'dabbrev-expand)
298 ;;;??? Do we want this?
299 ;;;###autoload
300 (define-key esc-map [?\C-/] 'dabbrev-completion)
302 ;;;###autoload
303 (defun dabbrev-completion (&optional arg)
304 "Completion on current word.
305 Like \\[dabbrev-expand] but finds all expansions in the current buffer
306 and presents suggestions for completion.
308 With a prefix argument, it searches all buffers accepted by the
309 function pointed out by `dabbrev-friend-buffer-function' to find the
310 completions.
312 If the prefix argument is 16 (which comes from C-u C-u),
313 then it searches *all* buffers.
315 With no prefix argument, it reuses an old completion list
316 if there is a suitable one already."
318 (interactive "*P")
319 (let* ((dabbrev-always-check-other-buffers (and arg t))
320 (dabbrev-check-rest-of-buffers
321 (and arg (= (prefix-numeric-value arg) 16)))
322 (abbrev (dabbrev--abbrev-at-point))
323 (ignore-case-p (and (eval dabbrev-case-fold-search)
324 (or (not dabbrev-upcase-means-case-search)
325 (string= abbrev (downcase abbrev)))))
326 (my-obarray dabbrev--last-obarray)
327 init)
328 (save-excursion
329 (if (and (null arg)
330 my-obarray
331 (or (eq dabbrev--last-completion-buffer (current-buffer))
332 (and (window-minibuffer-p (selected-window))
333 (eq dabbrev--last-completion-buffer
334 (dabbrev--minibuffer-origin))))
335 dabbrev--last-abbreviation
336 (>= (length abbrev) (length dabbrev--last-abbreviation))
337 (string= dabbrev--last-abbreviation
338 (substring abbrev 0
339 (length dabbrev--last-abbreviation)))
340 (setq init (try-completion abbrev my-obarray)))
341 ;; We can reuse the existing completion list.
343 ;;--------------------------------
344 ;; New abbreviation to expand.
345 ;;--------------------------------
346 (dabbrev--reset-global-variables)
347 (setq dabbrev--last-abbreviation abbrev)
348 ;; Find all expansion
349 (let ((completion-list
350 (dabbrev--find-all-expansions abbrev ignore-case-p)))
351 ;; Make an obarray with all expansions
352 (setq my-obarray (make-vector (length completion-list) 0))
353 (or (> (length my-obarray) 0)
354 (error "No dynamic expansion for \"%s\" found%s"
355 abbrev
356 (if dabbrev--check-other-buffers "" " in this-buffer")))
357 (cond
358 ((or (not ignore-case-p)
359 (not dabbrev-case-replace))
360 (mapc (function (lambda (string)
361 (intern string my-obarray)))
362 completion-list))
363 ((string= abbrev (upcase abbrev))
364 (mapc (function (lambda (string)
365 (intern (upcase string) my-obarray)))
366 completion-list))
367 ((string= (substring abbrev 0 1)
368 (upcase (substring abbrev 0 1)))
369 (mapc (function (lambda (string)
370 (intern (dabbrev--capitalize string) my-obarray)))
371 completion-list))
373 (mapc (function (lambda (string)
374 (intern (downcase string) my-obarray)))
375 completion-list)))
376 (setq dabbrev--last-obarray my-obarray)
377 (setq dabbrev--last-completion-buffer (current-buffer))
378 ;; Find the longest common string.
379 (setq init (try-completion abbrev my-obarray)))))
380 ;;--------------------------------
381 ;; Let the user choose between the expansions
382 ;;--------------------------------
383 (or (stringp init)
384 (setq init abbrev))
385 (cond
386 ;; * Replace string fragment with matched common substring completion.
387 ((and (not (string-equal init ""))
388 (not (string-equal (downcase init) (downcase abbrev))))
389 (if (> (length (all-completions init my-obarray)) 1)
390 (message "Repeat `%s' to see all completions"
391 (key-description (this-command-keys)))
392 (message "The only possible completion"))
393 (dabbrev--substitute-expansion nil abbrev init))
395 ;; * String is a common substring completion already. Make list.
396 (message "Making completion list...")
397 (with-output-to-temp-buffer " *Completions*"
398 (display-completion-list (all-completions init my-obarray)))
399 (message "Making completion list...done")))
400 (and (window-minibuffer-p (selected-window))
401 (message nil))))
403 ;;;###autoload
404 (defun dabbrev-expand (arg)
405 "Expand previous word \"dynamically\".
407 Expands to the most recent, preceding word for which this is a prefix.
408 If no suitable preceding word is found, words following point are
409 considered. If still no suitable word is found, then look in the
410 buffers accepted by the function pointed out by variable
411 `dabbrev-friend-buffer-function'.
413 A positive prefix argument, N, says to take the Nth backward *distinct*
414 possibility. A negative argument says search forward.
416 If the cursor has not moved from the end of the previous expansion and
417 no argument is given, replace the previously-made expansion
418 with the next possible expansion not yet tried.
420 The variable `dabbrev-backward-only' may be used to limit the
421 direction of search to backward if set non-nil.
424 To make it more powerful, make sure that
425 `dabbrev-always-check-other-buffers' is set to t.
427 See also `dabbrev-abbrev-char-regexp' and \\[dabbrev-completion]."
428 (interactive "*P")
429 (let (abbrev expansion old direction)
430 ;; abbrev -- the abbrev to expand
431 ;; expansion -- the expansion found (eventually) or nil until then
432 ;; old -- the text currently in the buffer
433 ;; (the abbrev, or the previously-made expansion)
434 (save-excursion
435 (if (and (null arg)
436 dabbrev--last-abbrev-location
437 (or (eq last-command this-command)
438 (and (window-minibuffer-p (selected-window))
439 (= dabbrev--last-abbrev-location
440 (point)))))
441 ;; Find a different expansion for the same abbrev as last time.
442 (progn
443 (setq abbrev dabbrev--last-abbreviation)
444 (setq old dabbrev--last-expansion)
445 (setq direction dabbrev--last-direction))
446 ;; We have a different abbrev to expand.
447 (dabbrev--reset-global-variables)
448 (setq direction (if (null arg)
449 (if dabbrev-backward-only 1 0)
450 (prefix-numeric-value arg)))
451 (setq abbrev (dabbrev--abbrev-at-point))
452 (setq old nil))
454 ;;--------------------------------
455 ;; Find the expansion
456 ;;--------------------------------
457 (setq expansion
458 (dabbrev--find-expansion abbrev direction
459 (and (eval dabbrev-case-fold-search)
460 (or (not dabbrev-upcase-means-case-search)
461 (string= abbrev (downcase abbrev)))))))
462 (cond
463 ((not expansion)
464 (dabbrev--reset-global-variables)
465 (if old
466 (save-excursion
467 (search-backward (substring old (length abbrev)))
468 (delete-region (match-beginning 0) (match-end 0))))
469 (error "No%s dynamic expansion for `%s' found"
470 (if old " further" "") abbrev))
472 (if (not (eq dabbrev--last-buffer dabbrev--last-buffer-found))
473 (progn
474 (message "Expansion found in '%s'"
475 (buffer-name dabbrev--last-buffer))
476 (setq dabbrev--last-buffer-found dabbrev--last-buffer))
477 (message nil))
478 ;; Success: stick it in and return.
479 (dabbrev--substitute-expansion old abbrev expansion)
480 ;; Save state for re-expand.
481 (setq dabbrev--last-expansion expansion)
482 (setq dabbrev--last-abbreviation abbrev)
483 (setq dabbrev--last-abbrev-location (point-marker))))))
485 ;;;----------------------------------------------------------------
486 ;;;----------------------------------------------------------------
487 ;;; Local functions
488 ;;;----------------------------------------------------------------
489 ;;;----------------------------------------------------------------
491 (defun dabbrev--capitalize (string)
492 ;; Capitalize STRING (See capitalize-word)
493 (let ((new-string ""))
494 (save-match-data
495 (while (string-match "\\w+" string)
496 (let* ((mb (match-beginning 0))
497 (me (match-end 0))
498 (ms (substring string mb me)))
499 (setq new-string
500 (concat new-string
501 (substring string 0 mb)
502 (upcase (substring ms 0 1))
503 (downcase (substring ms 1))))
504 (setq string (substring string me)))))
505 new-string))
507 ;;; Checks if OTHER-BUFFER has the same major mode as current buffer.
508 (defun dabbrev--same-major-mode-p (other-buffer)
509 (eq major-mode
510 (save-excursion
511 (set-buffer other-buffer)
512 major-mode)))
514 ;;; Back over all abbrev type characters and then moves forward over
515 ;;; all skip characters.
516 (defun dabbrev--goto-start-of-abbrev ()
517 ;; Move backwards over abbrev chars
518 (save-match-data
519 (if (not (bobp))
520 (progn
521 (forward-char -1)
522 (while (and (looking-at dabbrev--abbrev-char-regexp)
523 (not (bobp)))
524 (forward-char -1))
525 (or (looking-at dabbrev--abbrev-char-regexp)
526 (forward-char 1))))
527 (and dabbrev-abbrev-skip-leading-regexp
528 (while (looking-at dabbrev-abbrev-skip-leading-regexp)
529 (forward-char 1)))))
531 ;;; Extract the symbol at point to serve as abbreviation.
532 (defun dabbrev--abbrev-at-point ()
533 ;; Check for error
534 (save-excursion
535 (save-match-data
536 (if (or (bobp)
537 (progn
538 (forward-char -1)
539 (not (looking-at (concat "\\("
540 (or dabbrev-abbrev-char-regexp
541 "\\sw\\|\\s_")
542 "\\)+")))))
543 (error "Not positioned immediately after an abbreviation"))))
544 ;; Return abbrev at point
545 (save-excursion
546 (setq dabbrev--last-abbrev-location (point))
547 (buffer-substring (point)
548 (progn (dabbrev--goto-start-of-abbrev)
549 (point)))))
551 ;;; Initializes all global variables
552 (defun dabbrev--reset-global-variables ()
553 ;; dabbrev--last-obarray and dabbrev--last-completion-buffer
554 ;; must not be reset here.
555 (setq dabbrev--last-table nil
556 dabbrev--last-abbreviation nil
557 dabbrev--last-abbrev-location nil
558 dabbrev--last-direction nil
559 dabbrev--last-expansion nil
560 dabbrev--last-expansion-location nil
561 dabbrev--friend-buffer-list nil
562 dabbrev--last-buffer nil
563 dabbrev--last-buffer-found nil
564 dabbrev--abbrev-char-regexp (or dabbrev-abbrev-char-regexp
565 "\\sw\\|\\s_")
566 dabbrev--check-other-buffers dabbrev-always-check-other-buffers))
568 ;;; Find all buffers that are considered "friends" according to the
569 ;;; function pointed out by dabbrev-friend-buffer-function.
570 (defun dabbrev--select-buffers ()
571 (save-excursion
572 (and (window-minibuffer-p (selected-window))
573 (set-buffer (dabbrev--minibuffer-origin)))
574 (let ((orig-buffer (current-buffer)))
575 (dabbrev-filter-elements
576 buffer (buffer-list)
577 (and (not (eq orig-buffer buffer))
578 (boundp 'dabbrev-friend-buffer-function)
579 (funcall dabbrev-friend-buffer-function buffer))))))
581 ;;; Search for ABBREV, N times, normally looking forward,
582 ;;; but looking in reverse instead if REVERSE is non-nil.
583 (defun dabbrev--try-find (abbrev reverse n ignore-case)
584 (save-excursion
585 (let ((expansion nil))
586 (and dabbrev--last-expansion-location
587 (goto-char dabbrev--last-expansion-location))
588 (let ((case-fold-search ignore-case)
589 (count n))
590 (while (and (> count 0)
591 (setq expansion (dabbrev--search abbrev
592 reverse
593 ignore-case)))
594 (setq count (1- count))))
595 (and expansion
596 (setq dabbrev--last-expansion-location (point)))
597 expansion)))
599 ;;; Find all expansions of ABBREV
600 (defun dabbrev--find-all-expansions (abbrev ignore-case)
601 (let ((all-expansions nil)
602 expansion)
603 (save-excursion
604 (goto-char (point-min))
605 (while (setq expansion (dabbrev--find-expansion abbrev -1 ignore-case))
606 (push expansion all-expansions)))
607 all-expansions))
609 (defun dabbrev--scanning-message ()
610 (message "Scanning `%s'" (buffer-name (current-buffer))))
612 ;;; Find one occasion of ABBREV.
613 ;;; DIRECTION > 0 means look that many times backwards.
614 ;;; DIRECTION < 0 means look that many times forward.
615 ;;; DIRECTION = 0 means try both backward and forward.
616 ;;; IGNORE-CASE non-nil means ignore case when searching.
617 (defun dabbrev--find-expansion (abbrev direction ignore-case)
618 (let (expansion)
619 (save-excursion
620 (cond
621 (dabbrev--last-buffer
622 (set-buffer dabbrev--last-buffer)
623 (dabbrev--scanning-message))
624 ((and (not dabbrev-search-these-buffers-only)
625 (window-minibuffer-p (selected-window)))
626 (set-buffer (dabbrev--minibuffer-origin))
627 ;; In the minibuffer-origin buffer we will only search from
628 ;; the top and down.
629 (goto-char (point-min))
630 (setq direction -1)
631 (dabbrev--scanning-message)))
632 (cond
633 ;; ------------------------------------------
634 ;; Look backwards
635 ;; ------------------------------------------
636 ((and (not dabbrev-search-these-buffers-only)
637 (>= direction 0)
638 (setq dabbrev--last-direction (min 1 direction))
639 (setq expansion (dabbrev--try-find abbrev t
640 (max 1 direction)
641 ignore-case)))
642 expansion)
643 ;; ------------------------------------------
644 ;; Look forward
645 ;; ------------------------------------------
646 ((and (or (not dabbrev-search-these-buffers-only)
647 dabbrev--last-buffer)
648 (<= direction 0)
649 (setq dabbrev--last-direction -1)
650 (setq expansion (dabbrev--try-find abbrev nil
651 (max 1 (- direction))
652 ignore-case)))
653 expansion)
654 ;; ------------------------------------------
655 ;; Look in other buffers.
656 ;; Start at (point-min) and look forward.
657 ;; ------------------------------------------
659 (setq dabbrev--last-direction -1)
660 ;; Make sure that we should check other buffers
661 (or dabbrev--friend-buffer-list
662 dabbrev--last-buffer
663 (setq dabbrev--friend-buffer-list
664 (mapcar (function get-buffer)
665 dabbrev-search-these-buffers-only))
666 (not dabbrev--check-other-buffers)
667 (not (or (eq dabbrev--check-other-buffers t)
668 (progn
669 (setq dabbrev--check-other-buffers
670 (y-or-n-p "Scan other buffers also? ")))))
671 (let* (friend-buffer-list non-friend-buffer-list)
672 (setq dabbrev--friend-buffer-list
673 (funcall dabbrev-select-buffers-function))
674 (if dabbrev-check-rest-of-buffers
675 (setq non-friend-buffer-list
676 (nreverse
677 (dabbrev-filter-elements
678 buffer (buffer-list)
679 (not (memq buffer dabbrev--friend-buffer-list))))
680 dabbrev--friend-buffer-list
681 (append dabbrev--friend-buffer-list
682 non-friend-buffer-list)))))
683 ;; Walk through the buffers
684 (while (and (not expansion) dabbrev--friend-buffer-list)
685 (setq dabbrev--last-buffer
686 (car dabbrev--friend-buffer-list))
687 (setq dabbrev--friend-buffer-list
688 (cdr dabbrev--friend-buffer-list))
689 (set-buffer dabbrev--last-buffer)
690 (dabbrev--scanning-message)
691 (setq dabbrev--last-expansion-location (point-min))
692 (setq expansion (dabbrev--try-find abbrev nil 1 ignore-case)))
693 expansion)))))
695 (defun dabbrev--safe-replace-match (string &optional fixedcase literal)
696 (if (eq major-mode 'picture-mode)
697 (picture-replace-match string fixedcase literal)
698 (replace-match string fixedcase literal)))
700 ;;;----------------------------------------------------------------
701 ;;; Substitute the current string in buffer with the expansion
702 ;;; OLD is nil or the last expansion substring.
703 ;;; ABBREV is the abbreviation we are working with.
704 ;;; EXPANSION is the expansion substring.
705 (defun dabbrev--substitute-expansion (old abbrev expansion)
706 ;;(undo-boundary)
707 (let ((use-case-replace (and (eval dabbrev-case-fold-search)
708 (or (not dabbrev-upcase-means-case-search)
709 (string= abbrev (downcase abbrev)))
710 (eval dabbrev-case-replace))))
711 (and nil use-case-replace
712 (setq old (concat abbrev (or old "")))
713 (setq expansion (concat abbrev expansion)))
714 (if old
715 (save-excursion
716 (search-backward old))
717 ;;(store-match-data (list (point-marker) (point-marker)))
718 (search-backward abbrev))
719 ;; Make case of replacement conform to case of abbreviation
720 ;; provided (1) that kind of thing is enabled in this buffer
721 ;; and (2) the replacement itself is all lower case.
722 (dabbrev--safe-replace-match expansion
723 (not use-case-replace)
724 t)))
727 ;;;----------------------------------------------------------------
728 ;;; Search function used by dabbrevs library.
730 ;;; ABBREV is string to find as prefix of word. Second arg, REVERSE,
731 ;;; is t for reverse search, nil for forward. Variable dabbrev-limit
732 ;;; controls the maximum search region size. Third argment IGNORE-CASE
733 ;;; non-nil means treat case as insignificant while looking for a match
734 ;;; and when comparing with previous matches. Also if that's non-nil
735 ;;; and the match is found at the beginning of a sentence and is in
736 ;;; lower case except for the initial then it is converted to all lower
737 ;;; case for return.
739 ;;; Table of expansions already seen is examined in buffer
740 ;;; `dabbrev--last-table' so that only distinct possibilities are found
741 ;;; by dabbrev-re-expand.
743 ;;; Value is the expansion, or nil if not found.
745 (defun dabbrev--search (abbrev reverse ignore-case)
746 (save-match-data
747 (let ((pattern1 (concat (regexp-quote abbrev)
748 "\\(" dabbrev--abbrev-char-regexp "\\)"))
749 (pattern2 (concat (regexp-quote abbrev)
750 "\\(\\(" dabbrev--abbrev-char-regexp "\\)+\\)"))
751 (found-string nil))
752 ;; Limited search.
753 (save-restriction
754 (and dabbrev-limit
755 (narrow-to-region dabbrev--last-expansion-location
756 (+ (point)
757 (if reverse (- dabbrev-limit) dabbrev-limit))))
758 ;;--------------------------------
759 ;; Look for a distinct expansion, using dabbrev--last-table.
760 ;;--------------------------------
761 (while (and (not found-string)
762 (if reverse
763 (re-search-backward pattern1 nil t)
764 (re-search-forward pattern1 nil t)))
765 (cond
766 ((progn
767 (goto-char (match-beginning 0))
768 (dabbrev--goto-start-of-abbrev)
769 (/= (point) (match-beginning 0)))
770 ;; Prefix of found abbreviation not OK
771 nil)
773 (goto-char (match-beginning 0))
774 (re-search-forward pattern2)
775 (setq found-string
776 (buffer-substring (match-beginning 1) (match-end 1)))
777 (and ignore-case (setq found-string (downcase found-string)))
778 ;; Throw away if found in table
779 (if (dabbrev-filter-elements
780 table-string dabbrev--last-table
781 (string= found-string table-string))
782 (setq found-string nil))))
783 (if reverse
784 (goto-char (match-beginning 0))
785 (goto-char (match-end 0))))
786 (cond
787 (found-string
788 ;;--------------------------------
789 ;; Put in `dabbrev--last-table' and decide if we should return
790 ;; result or (downcase result)
791 ;;--------------------------------
792 (push found-string dabbrev--last-table)
793 (let ((result (buffer-substring (match-beginning 0) (match-end 0))))
794 (if (and ignore-case (eval dabbrev-case-replace))
795 (downcase result)
796 result))))))))
798 (provide 'dabbrev)
800 ;; dabbrev.el ends here