(set_search_regs): Really set search_regs.start and .end.
[bpt/emacs.git] / lisp / dabbrev.el
CommitLineData
7313ccdb
RS
1;;; dabbrev.el --- dynamic abbreviation package
2;; Copyright (C) 1985, 1986, 1992, 1994 Free Software Foundation, Inc.
2f790b20 3
6163b3ba
RS
4;; Author: Don Morrison
5;; Maintainer: Lars Lindberg <Lars.Lindberg@sypro.cap.se>
6;; Created: 16 Mars 1992
dd1ae355 7;; Lindberg's last update version: 5.2
6163b3ba 8;; Keywords: abbrev expand completion
3a801d0c 9
6163b3ba 10;; This program is free software; you can redistribute it and/or modify
2f790b20 11;; it under the terms of the GNU General Public License as published by
6163b3ba
RS
12;; the Free Software Foundation; either version 2 of the License, or
13;; (at your option) any later version.
14;;
15;; This program is distributed in the hope that it will be useful,
2f790b20
JB
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.
6163b3ba 19;;
2f790b20 20;; You should have received a copy of the GNU General Public License
6163b3ba
RS
21;; along with this program; if not, write to the Free Software
22;; Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
2f790b20 23
e5167999 24;;; Commentary:
2f790b20 25
6163b3ba
RS
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.
29;;
30;; To expand a word, just put the point right after the word and press
31;; M-/ (dabbrev-expand) or M-C-/ (dabbrev-completion).
32;;
33;; There are powerful things in this package that aren't turned on by
7313ccdb 34;; default. I recommend you to do the following.
6163b3ba
RS
35;;
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_")
39;;
40;; Dabbrev will now search in all buffers with the same major mode for
7313ccdb 41;; your expansions. It will also search for complete symbols, the old
6163b3ba
RS
42;; dabbrev package only looked half-heartedly for symbols.
43;;
44;; Check out the customizable variables below to learn about all the
45;; features of this package.
46
47;;; Hints and tips for major modes writers:
48
49;; Recommended values C/Lisp etc text
50;; dabbrev-case-fold-search nil t
51;; dabbrev-case-replace nil t
52;;
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.
56;;
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.
61
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.
65
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)))))
73
6163b3ba
RS
74
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.
80
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.
7313ccdb
RS
85;; - Check the kill-ring when everything else fails. (Maybe something
86;; for hippie-expand?). [Bng] <boris@cs.rochester.edu>
6163b3ba
RS
87
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.
2f790b20 101
e5167999 102;;; Code:
6163b3ba
RS
103
104;;;----------------------------------------------------------------
105;;;----------------------------------------------------------------
106;;; Customization variables
107;;;----------------------------------------------------------------
108;;;----------------------------------------------------------------
109(defvar dabbrev-backward-only nil
7313ccdb 110 "*If non-nil, `dabbrev-expand' only looks backwards.")
6163b3ba
RS
111
112(defvar dabbrev-limit nil
113 "*Limits region searched by `dabbrev-expand' to this many chars away.")
114
115(defvar dabbrev-abbrev-skip-leading-regexp nil
116 "*Regexp for skipping leading characters of an abbreviation.
117
7313ccdb
RS
118Example: Set this to \"\\\\$\" for programming languages
119in which variable names may appear with or without a leading `$'.
120(For example, in Makefiles.)
6163b3ba
RS
121
122Set this to nil if no characters should be skipped.")
123
124;; I recommend that you set this to nil.
125(defvar dabbrev-case-fold-search 'case-fold-search
7313ccdb
RS
126 "*Non-nil if dabbrev searches should ignore case.
127A value of nil means case is significant.
6163b3ba 128
7313ccdb
RS
129The value of this variable is an expression; it is evaluated
130and the resulting value determines the decision.
131For example: setting this to `case-fold-search' means evaluate that
132variable to see whether its value is nil.")
6163b3ba
RS
133
134(defvar dabbrev-upcase-means-case-search nil
135 "*The significance of an uppercase character in an abbreviation.
7313ccdb 136nil means case fold search, non-nil means case sensitive search.
6163b3ba 137
7313ccdb
RS
138This variable has an effect only when the value of
139`dabbrev-case-fold-search' evaluates to t.")
6163b3ba
RS
140
141;; I recommend that you set this to nil.
142(defvar dabbrev-case-replace 'case-replace
7313ccdb
RS
143 "*Non-nil means dabbrev should preserve case when expanding the abbreviation.
144The value of this variable is an expression; it is evaluated
145and the resulting value determines the decision.
146For example, setting this to `case-replace' means evaluate that
147variable to see if its value is t or nil.
6163b3ba 148
7313ccdb
RS
149This variable has an effect only when the value of
150`dabbrev-case-fold-search' evaluates to t.")
6163b3ba 151
5eb62982 152(defvar dabbrev-abbrev-char-regexp "\\sw\\|\\s_"
7313ccdb
RS
153 "*Regexp to recognize a character in an abbreviation or expansion.
154This regexp will be surrounded with \\\\( ... \\\\) when actually used.
6163b3ba 155
7313ccdb 156Set this variable to \"\\\\sw\" if you want ordinary words or
6163b3ba
RS
157\"\\\\sw\\\\|\\\\s_\" if you want symbols.
158
159You can also set it to nil if you want old-style dabbrev searching
7313ccdb 160\(the abbreviation is from point to previous word-start, the
6163b3ba
RS
161search is for symbols).
162
7313ccdb
RS
163For instance, if you are programming in Lisp, `yes-or-no-p' is a symbol,
164while `yes', `or', `no' and `p' are considered words. If you set this
165variable to nil, then expanding `yes-or-no-' looks for a symbol
166starting 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
169expanding `yes-or-no-' signals an error because `-' is not part of a word;
170but expanding `yes-or-no' looks for a word starting with `no'.
6163b3ba
RS
171
172The recommended value is \"\\\\sw\\\\|\\\\s_\".")
173
7313ccdb
RS
174(defvar dabbrev-check-rest-of-buffers t
175 "*Non-nil means dabbrev package should search in all buffers.
176It searches the buffers pointed out by `dabbrev-select-buffers-function'
177first; afterward it looks in the rest of the buffers.")
6163b3ba
RS
178
179
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?\
183nil = Don't look in other buffers.\n\
184t = Look in other buffers.\n\
185Value other than nil and t = ask the user if he want's to look in
186other buffers.
187
188The recommended value is t.")
189
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.
195
196The function should take no arguments and return a list of buffers to
197search for expansions. Have a look at `dabbrev--select-buffers' for
198an example.
199
200A mode setting this variable should make it buffer local.")
201
202(defvar dabbrev-friend-buffer-function 'dabbrev--same-major-mode-p
203 "*A function to check if OTHER-BUFFER should be searched by dabbrev.
204
205The function should take one argument, OTHER-BUFFER, and return
206non-nil if that buffer should be searched. Have a look at
207`dabbrev--same-major-mode-p' for an example.
208
7313ccdb
RS
209The value of `dabbrev-friend-buffer-function' has an effect only if
210the value of `dabbrev-select-buffers-function' uses it. The function
211`dabbrev--select-buffers' is one function you can use here.
6163b3ba
RS
212
213A mode setting this variable should make it buffer local.")
214
215(defvar dabbrev-search-these-buffers-only nil
7313ccdb
RS
216 "If non-nil, a list of buffers which dabbrev should search.
217If this variable is non-nil, dabbrev will only look in these buffers.
218It will not even look in the current buffer if it is not a member of
219this list.")
e5167999 220
6163b3ba
RS
221;;;----------------------------------------------------------------
222;;;----------------------------------------------------------------
223;;; Internal variables
224;;;----------------------------------------------------------------
225;;;----------------------------------------------------------------
2f790b20 226
6163b3ba
RS
227;; Last obarray of completions in `dabbrev-completion'
228(defvar dabbrev--last-obarray nil)
2f790b20 229
6163b3ba
RS
230;; Table of expansions seen so far
231(defvar dabbrev--last-table nil)
2f790b20 232
6163b3ba
RS
233;; Last string we tried to expand.
234(defvar dabbrev--last-abbreviation nil)
2f790b20 235
6163b3ba
RS
236;; Location last abbreviation began
237(defvar dabbrev--last-abbrev-location nil)
2f790b20 238
6163b3ba
RS
239;; Direction of last dabbrevs search
240(defvar dabbrev--last-direction 0)
2f790b20 241
6163b3ba
RS
242;; Last expansion of an abbreviation.
243(defvar dabbrev--last-expansion nil)
2f790b20 244
6163b3ba
RS
245;; Location the last expansion was found.
246(defvar dabbrev--last-expansion-location nil)
247
248;; The list of remaining buffers with the same mode as current buffer.
249(defvar dabbrev--friend-buffer-list nil)
250
251;; The buffer we looked in last.
252(defvar dabbrev--last-buffer nil)
253
254;; The buffer we found the expansion last time.
255(defvar dabbrev--last-buffer-found nil)
256
257;; The buffer we last did a completion in.
258(defvar dabbrev--last-completion-buffer nil)
259
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)
262
263;; The regexp for recognizing a character in an abbreviation.
264(defvar dabbrev--abbrev-char-regexp nil)
265
266;;;----------------------------------------------------------------
267;;;----------------------------------------------------------------
268;;; Macros
269;;;----------------------------------------------------------------
270;;;----------------------------------------------------------------
271
272;;; Get the buffer that mini-buffer was activated from
273(defsubst dabbrev--minibuffer-origin ()
274 (car (cdr (buffer-list))))
275
7313ccdb
RS
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))))
289
6163b3ba
RS
290;;;----------------------------------------------------------------
291;;;----------------------------------------------------------------
292;;; Exported functions
293;;;----------------------------------------------------------------
294;;;----------------------------------------------------------------
295
296;;;###autoload
297(define-key esc-map "/" 'dabbrev-expand)
7313ccdb 298;;;??? Do we want this?
6163b3ba 299;;;###autoload
7313ccdb 300(define-key esc-map [?\C-/] 'dabbrev-completion))
6163b3ba
RS
301
302;;;###autoload
303(defun dabbrev-completion (&optional arg)
304 "Completion on current word.
6163b3ba
RS
305Like \\[dabbrev-expand] but finds all expansions in the current buffer
306and presents suggestions for completion.
307
7313ccdb
RS
308With a prefix argument, it searches all buffers accepted by the
309function pointed out by `dabbrev-friend-buffer-function' to find the
dd1ae355
RS
310completions.
311
312If the prefix argument is 16 (which comes from C-u C-u),
313then it searches *all* buffers.
6163b3ba 314
7313ccdb
RS
315With no prefix argument, it reuses an old completion list
316if there is a suitable one already."
6163b3ba
RS
317
318 (interactive "*P")
319 (let* ((dabbrev-always-check-other-buffers (and arg t))
dd1ae355
RS
320 (dabbrev-check-rest-of-buffers
321 (and arg (= (prefix-numeric-value arg) 16)))
6163b3ba
RS
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)))
7313ccdb
RS
341 ;; We can reuse the existing completion list.
342 nil
6163b3ba
RS
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)
7313ccdb 354 (error "No dynamic expansion for \"%s\" found%s"
6163b3ba
RS
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))
372 (t
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)
7313ccdb
RS
390 (message "Repeat `%s' to see all completions"
391 (key-description (this-command-keys)))
6163b3ba
RS
392 (message "The only possible completion"))
393 (dabbrev--substitute-expansion nil abbrev init))
394 (t
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)))
7313ccdb 399 (message "Making completion list...done")))
6163b3ba
RS
400 (and (window-minibuffer-p (selected-window))
401 (message nil))))
2f790b20
JB
402
403;;;###autoload
404(defun dabbrev-expand (arg)
405 "Expand previous word \"dynamically\".
2f790b20 406
6163b3ba
RS
407Expands to the most recent, preceding word for which this is a prefix.
408If no suitable preceding word is found, words following point are
409considered. If still no suitable word is found, then look in the
410buffers accepted by the function pointed out by variable
411`dabbrev-friend-buffer-function'.
2f790b20 412
7313ccdb 413A positive prefix argument, N, says to take the Nth backward *distinct*
6163b3ba 414possibility. A negative argument says search forward.
2f790b20
JB
415
416If the cursor has not moved from the end of the previous expansion and
417no argument is given, replace the previously-made expansion
6163b3ba
RS
418with the next possible expansion not yet tried.
419
420The variable `dabbrev-backward-only' may be used to limit the
421direction of search to backward if set non-nil.
422
7313ccdb 423???
6163b3ba
RS
424To make it more powerful, make sure that
425`dabbrev-always-check-other-buffers' is set to t.
426
7313ccdb 427See also `dabbrev-abbrev-char-regexp' and \\[dabbrev-completion]."
2f790b20 428 (interactive "*P")
6163b3ba 429 (let (abbrev expansion old direction)
2f790b20
JB
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)
2f790b20
JB
434 (save-excursion
435 (if (and (null arg)
6163b3ba
RS
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)))))
7313ccdb 441 ;; Find a different expansion for the same abbrev as last time.
6163b3ba
RS
442 (progn
443 (setq abbrev dabbrev--last-abbreviation)
444 (setq old dabbrev--last-expansion)
445 (setq direction dabbrev--last-direction))
7313ccdb 446 ;; We have a different abbrev to expand.
6163b3ba
RS
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))
453
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))))
7313ccdb 469 (error "No%s dynamic expansion for `%s' found"
6163b3ba
RS
470 (if old " further" "") abbrev))
471 (t
472 (if (not (eq dabbrev--last-buffer dabbrev--last-buffer-found))
2f790b20 473 (progn
6163b3ba
RS
474 (message "Expansion found in '%s'"
475 (buffer-name dabbrev--last-buffer))
476 (setq dabbrev--last-buffer-found dabbrev--last-buffer))
477 (message nil))
2f790b20 478 ;; Success: stick it in and return.
6163b3ba 479 (dabbrev--substitute-expansion old abbrev expansion)
2f790b20 480 ;; Save state for re-expand.
6163b3ba
RS
481 (setq dabbrev--last-expansion expansion)
482 (setq dabbrev--last-abbreviation abbrev)
483 (setq dabbrev--last-abbrev-location (point-marker))))))
484
6163b3ba
RS
485;;;----------------------------------------------------------------
486;;;----------------------------------------------------------------
487;;; Local functions
488;;;----------------------------------------------------------------
489;;;----------------------------------------------------------------
490
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))
506
507;;; Checks if OTHER-BUFFER has the same major mode as current buffer.
508(defun dabbrev--same-major-mode-p (other-buffer)
7313ccdb
RS
509 (eq major-mode
510 (save-excursion
511 (set-buffer other-buffer)
512 major-mode)))
6163b3ba
RS
513
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
7313ccdb
RS
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))))
6163b3ba
RS
527 (and dabbrev-abbrev-skip-leading-regexp
528 (while (looking-at dabbrev-abbrev-skip-leading-regexp)
529 (forward-char 1)))))
530
7313ccdb 531;;; Extract the symbol at point to serve as abbreviation.
6163b3ba
RS
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 "\\)+")))))
7313ccdb 543 (error "Not positioned immediately after an abbreviation"))))
6163b3ba
RS
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)))))
550
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))
567
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)))
7313ccdb
RS
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))))))
580
581;;; Search for ABBREV, N times, normally looking forward,
582;;; but looking in reverse instead if REVERSE is non-nil.
6163b3ba
RS
583(defun dabbrev--try-find (abbrev reverse n ignore-case)
584 (save-excursion
7313ccdb 585 (let ((expansion nil))
6163b3ba
RS
586 (and dabbrev--last-expansion-location
587 (goto-char dabbrev--last-expansion-location))
7313ccdb
RS
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))))
6163b3ba
RS
595 (and expansion
596 (setq dabbrev--last-expansion-location (point)))
597 expansion)))
598
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))
608
609(defun dabbrev--scanning-message ()
7313ccdb 610 (message "Scanning `%s'" (buffer-name (current-buffer))))
6163b3ba
RS
611
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 ;; ------------------------------------------
658 (t
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
7313ccdb 670 (y-or-n-p "Scan other buffers also? ")))))
6163b3ba
RS
671 (let* (friend-buffer-list non-friend-buffer-list)
672 (setq dabbrev--friend-buffer-list
673 (funcall dabbrev-select-buffers-function))
7313ccdb
RS
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)))))
6163b3ba
RS
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)))))
694
6163b3ba
RS
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)))
699
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)))
725
726
727;;;----------------------------------------------------------------
728;;; Search function used by dabbrevs library.
729
7313ccdb 730;;; ABBREV is string to find as prefix of word. Second arg, REVERSE,
6163b3ba
RS
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.
738
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.
742
743;;; Value is the expansion, or nil if not found.
744
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)
772 (t
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
7313ccdb
RS
779 (if (dabbrev-filter-elements
780 table-string dabbrev--last-table
781 (string= found-string table-string))
782 (setq found-string nil))))
6163b3ba
RS
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))))))))
797
01987a6b 798(provide 'dabbrev)
7313ccdb
RS
799
800;; dabbrev.el ends here
6163b3ba 801
49116ac0 802