comparison lisp/packages/dabbrev.el @ 0:376386a54a3c r19-14

Import from CVS: tag r19-14
author cvs
date Mon, 13 Aug 2007 08:45:50 +0200
parents
children ac2d302a0011
comparison
equal deleted inserted replaced
-1:000000000000 0:376386a54a3c
1 ;;; dabbrev.el --- dynamic abbreviation package
2 ;; Copyright (C) 1985, 1986, 1992, 1994 Free Software Foundation, Inc.
3
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.7
8 ;; Keywords: abbrev expand completion
9
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.
14 ;;
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.
19 ;;
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.
23
24 ;;; Synched up with: FSF 19.30.
25
26 ;;; Commentary:
27
28 ;; The purpose with this package is to let you write just a few
29 ;; characters of words you've written earlier to be able to expand
30 ;; them.
31 ;;
32 ;; To expand a word, just put the point right after the word and press
33 ;; M-/ (dabbrev-expand) or M-C-/ (dabbrev-completion).
34 ;;
35 ;; Check out the customizable variables below to learn about all the
36 ;; features of this package.
37
38 ;;; Hints and tips for major modes writers:
39
40 ;; Recommended values C/Lisp etc text
41 ;; dabbrev-case-fold-search nil t
42 ;; dabbrev-case-replace nil t
43 ;;
44 ;; Set the variables you want special for your mode like this:
45 ;; (set (make-local-variable 'dabbrev-case-replace) nil)
46 ;; Then you don't interfer with other modes.
47 ;;
48 ;; If your mode handles buffers that refers to other buffers
49 ;; (i.e. compilation-mode, gud-mode), then try to set
50 ;; `dabbrev-select-buffers-function' or `dabbrev-friend-buffer-function'
51 ;; to a function that point out those buffers.
52
53 ;; Same goes for major-modes that are connected to other modes. There
54 ;; are for instance a number of mail-modes. One for reading, one for
55 ;; creating a new mail etc. Maybe those should be connected.
56
57 ;; Example for GNUS (when we write a reply, we want dabbrev to look in
58 ;; the article for expansion):
59 ;; (set (make-local-variable 'dabbrev-friend-buffer-function)
60 ;; (lambda (buffer)
61 ;; (save-excursion
62 ;; (set-buffer buffer)
63 ;; (memq major-mode '(news-reply-mode gnus-article-mode)))))
64
65
66 ;; Known bugs and limitations.
67 ;; - Possible to do several levels of `dabbrev-completion' in the
68 ;; minibuffer.
69 ;; - dabbrev-completion doesn't handle resetting the globals variables
70 ;; right. It resets them after finding the abbrev.
71
72 ;; Future enhancements
73 ;; - Check the tags-files? Like tags-complete?
74 ;; - Add the possibility of searching both forward and backward to
75 ;; the nearest expansion.
76 ;; - Check the kill-ring when everything else fails. (Maybe something
77 ;; for hippie-expand?). [Bng] <boris@cs.rochester.edu>
78
79 ;;; These people gave suggestions:
80 ;; [hymie] Hyman Rosen <marks!hymie@jyacc.jyacc.com>
81 ;; [burgett] Steve Burgett <burgett@bizet.eecs.berkeley.edu>
82 ;; [jules] Julian Gosnell <jules@x.co.uk>
83 ;; [kifer] Michael Kifer <kifer@sbcs.sunysb.edu>
84 ;; [ake] Ake Stenhoff <extaksf@aom.ericsson.se>
85 ;; [alon] Alon Albert <al%imercury@uunet.uu.net>
86 ;; [tromey] Tom Tromey <tromey@busco.lanl.gov>
87 ;; [Rolf] Rolf Schreiber <rolf@mathematik.uni-stuttgart.de>
88 ;; [Petri] Petri Raitio <per@tekla.fi>
89 ;; [ejb] Jay Berkenbilt <ejb@ERA.COM>
90 ;; [hawley] Bob Hawley <rth1@quartet.mt.att.com>
91 ;; ... and to all the people who have participated in the beta tests.
92
93 ;;; Code:
94
95 ;;;----------------------------------------------------------------
96 ;;;----------------------------------------------------------------
97 ;;; Customization variables
98 ;;;----------------------------------------------------------------
99 ;;;----------------------------------------------------------------
100 (defvar dabbrev-backward-only nil
101 "*If non-nil, `dabbrev-expand' only looks backwards.")
102
103 (defvar dabbrev-limit nil
104 "*Limits region searched by `dabbrev-expand' to this many chars away.")
105
106 (defvar dabbrev-abbrev-skip-leading-regexp nil
107 "*Regexp for skipping leading characters of an abbreviation.
108
109 Example: Set this to \"\\\\$\" for programming languages
110 in which variable names may appear with or without a leading `$'.
111 (For example, in Makefiles.)
112
113 Set this to nil if no characters should be skipped.")
114
115 ;; XEmacs change: The old defaults are just too obnoxious. Rarely
116 ;; do you actually want the case-folding behavior here, even though
117 ;; it's useful to have case-fold-search set to t most of the time.
118 (defvar dabbrev-case-fold-search nil ;;'case-fold-search
119 "*Non-nil if dabbrev searches should ignore case.
120 A value of nil means case is significant.
121
122 The value of this variable is an expression; it is evaluated
123 and the resulting value determines the decision.
124 For example: setting this to `case-fold-search' means evaluate that
125 variable to see whether its value is nil.")
126
127 (defvar dabbrev-upcase-means-case-search nil
128 "*The significance of an uppercase character in an abbreviation.
129 nil means case fold search, non-nil means case sensitive search.
130
131 This variable has an effect only when the value of
132 `dabbrev-case-fold-search' evaluates to t.")
133
134 ;; XEmacs change: likewise here.
135 ;; I recommend that you set this to nil.
136 (defvar dabbrev-case-replace nil ;;'case-replace
137 "*Non-nil means dabbrev should preserve case when expanding the abbreviation.
138 More precisely, it preserves the case pattern of the abbreviation as you
139 typed it--as opposed to the case pattern of the expansion that is copied.
140 The value of this variable is an expression; it is evaluated
141 and the resulting value determines the decision.
142 For example, setting this to `case-replace' means evaluate that
143 variable to see if its value is t or nil.
144
145 This variable has an effect only when the value of
146 `dabbrev-case-fold-search' evaluates to t.")
147
148 (defvar dabbrev-abbrev-char-regexp nil
149 "*Regexp to recognize a character in an abbreviation or expansion.
150 This regexp will be surrounded with \\\\( ... \\\\) when actually used.
151
152 Set this variable to \"\\\\sw\" if you want ordinary words or
153 \"\\\\sw\\\\|\\\\s_\" if you want symbols (including characters whose
154 syntax is \"symbol\" as well as those whose syntax is \"word\".
155
156 The value nil has a special meaning: the abbreviation is from point to
157 previous word-start, but the search is for symbols.
158
159 For instance, if you are programming in Lisp, `yes-or-no-p' is a symbol,
160 while `yes', `or', `no' and `p' are considered words. If this
161 variable is nil, then expanding `yes-or-no-' looks for a symbol
162 starting with or containing `no-'. If you set this variable to
163 \"\\\\sw\\\\|\\\\s_\", that expansion looks for a symbol starting with
164 `yes-or-no-'. Finally, if you set this variable to \"\\\\sw\", then
165 expanding `yes-or-no-' signals an error because `-' is not part of a word;
166 but expanding `yes-or-no' looks for a word starting with `no'.
167
168 The recommended value is \"\\\\sw\\\\|\\\\s_\".")
169
170 (defvar dabbrev-check-all-buffers t
171 "*Non-nil means dabbrev package should search *all* buffers.
172
173 Dabbrev always searches the current buffer first. Then, if
174 `dabbrev-check-other-buffers' says so, it searches the buffers
175 designated by `dabbrev-select-buffers-function'.
176
177 Then, if `dabbrev-check-all-buffers' is non-nil, dabbrev searches
178 all the other buffers.")
179
180 (defvar dabbrev-check-other-buffers t
181 "*Should \\[dabbrev-expand] look in other buffers?\
182
183 nil: Don't look in other buffers.
184 t: Also look for expansions in the buffers pointed out by
185 `dabbrev-select-buffers-function'.
186 Anything else: When we can't find any more expansions in
187 the current buffer, then ask the user whether to look in other
188 buffers too.
189
190 The default value is t.")
191
192 ;; I guess setting this to a function that selects all C- or C++-
193 ;; mode buffers would be a good choice for a debugging buffer,
194 ;; when debugging C- or C++-code.
195 (defvar dabbrev-select-buffers-function 'dabbrev--select-buffers
196 "A function that selects buffers that should be searched by dabbrev.
197 The function should take no arguments and return a list of buffers to
198 search for expansions. Have a look at `dabbrev--select-buffers' for
199 an example.
200
201 A mode setting this variable should make it buffer local.")
202
203 (defvar dabbrev-friend-buffer-function 'dabbrev--same-major-mode-p
204 "*A function to decide whether dabbrev should search OTHER-BUFFER.
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.
208
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.
212
213 A mode setting this variable should make it buffer local.")
214
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.")
220
221 ;;;----------------------------------------------------------------
222 ;;;----------------------------------------------------------------
223 ;;; Internal variables
224 ;;;----------------------------------------------------------------
225 ;;;----------------------------------------------------------------
226
227 ;; Last obarray of completions in `dabbrev-completion'
228 (defvar dabbrev--last-obarray nil)
229
230 ;; Table of expansions seen so far
231 (defvar dabbrev--last-table nil)
232
233 ;; Last string we tried to expand.
234 (defvar dabbrev--last-abbreviation nil)
235
236 ;; Location last abbreviation began
237 (defvar dabbrev--last-abbrev-location nil)
238
239 ;; Direction of last dabbrevs search
240 (defvar dabbrev--last-direction 0)
241
242 ;; Last expansion of an abbreviation.
243 (defvar dabbrev--last-expansion nil)
244
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-check-other-buffers, but is set for every expand.
261 (defvar dabbrev--check-other-buffers dabbrev-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
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
290 (defun dabbrev--extent-clicked-on (event extent user-data)
291 (let ((buffer (first user-data))
292 (point (second user-data))
293 (init (third user-data))
294 (wconfig (fourth user-data)))
295 (set-window-configuration wconfig)
296 (set-buffer buffer)
297 (goto-char point)
298 (dabbrev--substitute-expansion nil init (extent-string extent))))
299
300 ;;;----------------------------------------------------------------
301 ;;;----------------------------------------------------------------
302 ;;; Exported functions
303 ;;;----------------------------------------------------------------
304 ;;;----------------------------------------------------------------
305
306 ;; XEmacs changes:
307 ;;;###autoload
308 (define-key global-map [(meta /)] 'dabbrev-expand)
309 ;;;??? Do we want this?
310 ;;;###autoload
311 (define-key global-map [(meta control /)] 'dabbrev-completion)
312
313 ;;;###autoload
314 (defun dabbrev-completion (&optional arg)
315 "Completion on current word.
316 Like \\[dabbrev-expand] but finds all expansions in the current buffer
317 and presents suggestions for completion.
318
319 With a prefix argument, it searches all buffers accepted by the
320 function pointed out by `dabbrev-friend-buffer-function' to find the
321 completions.
322
323 If the prefix argument is 16 (which comes from C-u C-u),
324 then it searches *all* buffers.
325
326 With no prefix argument, it reuses an old completion list
327 if there is a suitable one already."
328
329 (interactive "*P")
330 (dabbrev--reset-global-variables)
331 (let* ((dabbrev-check-other-buffers (and arg t))
332 (dabbrev-check-all-buffers
333 (and arg (= (prefix-numeric-value arg) 16)))
334 (abbrev (dabbrev--abbrev-at-point))
335 (ignore-case-p (and (eval dabbrev-case-fold-search)
336 (or (not dabbrev-upcase-means-case-search)
337 (string= abbrev (downcase abbrev)))))
338 (my-obarray dabbrev--last-obarray)
339 init)
340 (save-excursion
341 (if (and (null arg)
342 my-obarray
343 (or (eq dabbrev--last-completion-buffer (current-buffer))
344 (and (window-minibuffer-p (selected-window))
345 (eq dabbrev--last-completion-buffer
346 (dabbrev--minibuffer-origin))))
347 dabbrev--last-abbreviation
348 (>= (length abbrev) (length dabbrev--last-abbreviation))
349 (string= dabbrev--last-abbreviation
350 (substring abbrev 0
351 (length dabbrev--last-abbreviation)))
352 (setq init (try-completion abbrev my-obarray)))
353 ;; We can reuse the existing completion list.
354 nil
355 ;;--------------------------------
356 ;; New abbreviation to expand.
357 ;;--------------------------------
358 (setq dabbrev--last-abbreviation abbrev)
359 ;; Find all expansion
360 (let ((completion-list
361 (dabbrev--find-all-expansions abbrev ignore-case-p)))
362 ;; Make an obarray with all expansions
363 (setq my-obarray (make-vector (length completion-list) 0))
364 (or (> (length my-obarray) 0)
365 (error "No dynamic expansion for \"%s\" found%s"
366 abbrev
367 (if dabbrev--check-other-buffers "" " in this-buffer")))
368 (cond
369 ((or (not ignore-case-p)
370 (not dabbrev-case-replace))
371 (mapcar (function (lambda (string)
372 (intern string my-obarray)))
373 completion-list))
374 ((string= abbrev (upcase abbrev))
375 (mapcar (function (lambda (string)
376 (intern (upcase string) my-obarray)))
377 completion-list))
378 ((string= (substring abbrev 0 1)
379 (upcase (substring abbrev 0 1)))
380 (mapcar (function (lambda (string)
381 (intern (capitalize string) my-obarray)))
382 completion-list))
383 (t
384 (mapcar (function (lambda (string)
385 (intern (downcase string) my-obarray)))
386 completion-list)))
387 (setq dabbrev--last-obarray my-obarray)
388 (setq dabbrev--last-completion-buffer (current-buffer))
389 ;; Find the longest common string.
390 (setq init (try-completion abbrev my-obarray)))))
391 ;;--------------------------------
392 ;; Let the user choose between the expansions
393 ;;--------------------------------
394 (or (stringp init)
395 (setq init abbrev))
396 (cond
397 ;; * Replace string fragment with matched common substring completion.
398 ((and (not (string-equal init ""))
399 (not (string-equal (downcase init) (downcase abbrev))))
400 (if (> (length (all-completions init my-obarray)) 1)
401 (message "Repeat `%s' to see all completions"
402 (key-description (this-command-keys)))
403 (message "The only possible completion"))
404 (dabbrev--substitute-expansion nil abbrev init))
405 (t
406 ;; * String is a common substring completion already. Make list.
407 (message "Making completion list...")
408 ;; construct the arg before calling `with-output-to-temp-buffer'
409 ;; because that changes the window config
410 (let ((arg (list (current-buffer)
411 (set-marker (make-marker) (point))
412 init
413 (current-window-configuration))))
414 (with-output-to-temp-buffer " *Completions*"
415 (display-completion-list (all-completions init my-obarray)
416 'dabbrev--extent-clicked-on
417 arg)))
418 (message "Making completion list...done")))
419 (and (window-minibuffer-p (selected-window))
420 (message nil))))
421
422 ;;;###autoload
423 (defun dabbrev-expand (arg)
424 "Expand previous word \"dynamically\".
425
426 Expands to the most recent, preceding word for which this is a prefix.
427 If no suitable preceding word is found, words following point are
428 considered. If still no suitable word is found, then look in the
429 buffers accepted by the function pointed out by variable
430 `dabbrev-friend-buffer-function'.
431
432 A positive prefix argument, N, says to take the Nth backward *distinct*
433 possibility. A negative argument says search forward.
434
435 If the cursor has not moved from the end of the previous expansion and
436 no argument is given, replace the previously-made expansion
437 with the next possible expansion not yet tried.
438
439 The variable `dabbrev-backward-only' may be used to limit the
440 direction of search to backward if set non-nil.
441
442 See also `dabbrev-abbrev-char-regexp' and \\[dabbrev-completion]."
443 (interactive "*P")
444 (let (abbrev expansion old direction (orig-point (point)))
445 ;; abbrev -- the abbrev to expand
446 ;; expansion -- the expansion found (eventually) or nil until then
447 ;; old -- the text currently in the buffer
448 ;; (the abbrev, or the previously-made expansion)
449 (save-excursion
450 (if (and (null arg)
451 (markerp dabbrev--last-abbrev-location)
452 (marker-position dabbrev--last-abbrev-location)
453 (or (eq last-command this-command)
454 (and (window-minibuffer-p (selected-window))
455 (= dabbrev--last-abbrev-location
456 (point)))))
457 ;; Find a different expansion for the same abbrev as last time.
458 (progn
459 (setq abbrev dabbrev--last-abbreviation)
460 (setq old dabbrev--last-expansion)
461 (setq direction dabbrev--last-direction))
462 ;; If the user inserts a space after expanding
463 ;; and then asks to expand again, always fetch the next word.
464 (if (and (eq (preceding-char) ?\ )
465 (markerp dabbrev--last-abbrev-location)
466 (marker-position dabbrev--last-abbrev-location)
467 (= (point) (1+ dabbrev--last-abbrev-location)))
468 (progn
469 ;; The "abbrev" to expand is just the space.
470 (setq abbrev " ")
471 (save-excursion
472 (if dabbrev--last-buffer
473 (set-buffer dabbrev--last-buffer))
474 ;; Find the end of the last "expansion" word.
475 (if (or (eq dabbrev--last-direction 1)
476 (and (eq dabbrev--last-direction 0)
477 (< dabbrev--last-expansion-location (point))))
478 (setq dabbrev--last-expansion-location
479 (+ dabbrev--last-expansion-location
480 (length dabbrev--last-expansion))))
481 (goto-char dabbrev--last-expansion-location)
482 ;; Take the following word, with intermediate separators,
483 ;; as our expansion this time.
484 (re-search-forward
485 (concat "\\(\\(" dabbrev--abbrev-char-regexp "\\)+\\)"))
486 (setq expansion
487 (buffer-substring dabbrev--last-expansion-location
488 (point)))
489
490 ;; Record the end of this expansion, in case we repeat this.
491 (setq dabbrev--last-expansion-location (point)))
492 ;; Indicate that dabbrev--last-expansion-location is
493 ;; at the end of the expansion.
494 (setq dabbrev--last-direction -1))
495
496 ;; We have a different abbrev to expand.
497 (dabbrev--reset-global-variables)
498 (setq direction (if (null arg)
499 (if dabbrev-backward-only 1 0)
500 (prefix-numeric-value arg)))
501 (setq abbrev (dabbrev--abbrev-at-point))
502 (setq old nil)))
503
504 ;;--------------------------------
505 ;; Find the expansion
506 ;;--------------------------------
507 (or expansion
508 (setq expansion
509 (dabbrev--find-expansion abbrev direction
510 (and (eval dabbrev-case-fold-search)
511 (or (not dabbrev-upcase-means-case-search)
512 (string= abbrev (downcase abbrev))))))))
513 (cond
514 ((not expansion)
515 (dabbrev--reset-global-variables)
516 (if old
517 (save-excursion
518 (setq buffer-undo-list (cons orig-point buffer-undo-list))
519 ;; Put back the original abbrev with its original case pattern.
520 (search-backward old)
521 (insert abbrev)
522 (delete-region (point) (+ (point) (length old)))))
523 (error "No%s dynamic expansion for `%s' found"
524 (if old " further" "") abbrev))
525 (t
526 (if (not (eq dabbrev--last-buffer dabbrev--last-buffer-found))
527 (progn
528 (message "Expansion found in '%s'"
529 (buffer-name dabbrev--last-buffer))
530 (setq dabbrev--last-buffer-found dabbrev--last-buffer))
531 (message nil))
532 (if (and (or (eq (current-buffer) dabbrev--last-buffer)
533 (null dabbrev--last-buffer))
534 (numberp dabbrev--last-expansion-location)
535 (and (> dabbrev--last-expansion-location (point))))
536 (setq dabbrev--last-expansion-location
537 (copy-marker dabbrev--last-expansion-location)))
538 ;; Success: stick it in and return.
539 (setq buffer-undo-list (cons orig-point buffer-undo-list))
540 (dabbrev--substitute-expansion old abbrev expansion)
541 ;; Save state for re-expand.
542 (setq dabbrev--last-expansion expansion)
543 (setq dabbrev--last-abbreviation abbrev)
544 (setq dabbrev--last-abbrev-location (point-marker))))))
545
546 ;;;----------------------------------------------------------------
547 ;;;----------------------------------------------------------------
548 ;;; Local functions
549 ;;;----------------------------------------------------------------
550 ;;;----------------------------------------------------------------
551
552 ;;; Checks if OTHER-BUFFER has the same major mode as current buffer.
553 (defun dabbrev--same-major-mode-p (other-buffer)
554 (eq major-mode
555 (save-excursion
556 (set-buffer other-buffer)
557 major-mode)))
558
559 ;;; Back over all abbrev type characters and then moves forward over
560 ;;; all skip characters.
561 (defun dabbrev--goto-start-of-abbrev ()
562 ;; Move backwards over abbrev chars
563 (save-match-data
564 (if (not (bobp))
565 (progn
566 (forward-char -1)
567 (while (and (looking-at dabbrev--abbrev-char-regexp)
568 (not (bobp)))
569 (forward-char -1))
570 (or (looking-at dabbrev--abbrev-char-regexp)
571 (forward-char 1))))
572 (and dabbrev-abbrev-skip-leading-regexp
573 (while (looking-at dabbrev-abbrev-skip-leading-regexp)
574 (forward-char 1)))))
575
576 ;;; Extract the symbol at point to serve as abbreviation.
577 (defun dabbrev--abbrev-at-point ()
578 ;; Check for error
579 (if (bobp)
580 (error "No possible abbreviation preceding point"))
581 ;; Return abbrev at point
582 (save-excursion
583 ;; Record the end of the abbreviation.
584 (setq dabbrev--last-abbrev-location (point))
585 ;; If we aren't right after an abbreviation,
586 ;; move point back to just after one.
587 ;; This is so the user can get successive words
588 ;; by typing the punctuation followed by M-/.
589 (save-match-data
590 (if (save-excursion
591 (forward-char -1)
592 (not (looking-at (concat "\\("
593 (or dabbrev-abbrev-char-regexp
594 "\\sw\\|\\s_")
595 "\\)+"))))
596 (if (re-search-backward (or dabbrev-abbrev-char-regexp
597 "\\sw\\|\\s_")
598 nil t)
599 (forward-char 1)
600 (error "No possible abbreviation preceding point"))))
601 ;; Now find the beginning of that one.
602 (dabbrev--goto-start-of-abbrev)
603 (buffer-substring dabbrev--last-abbrev-location
604 (point))))
605
606 ;;; Initializes all global variables
607 (defun dabbrev--reset-global-variables ()
608 ;; dabbrev--last-obarray and dabbrev--last-completion-buffer
609 ;; must not be reset here.
610 (setq dabbrev--last-table nil
611 dabbrev--last-abbreviation nil
612 dabbrev--last-abbrev-location nil
613 dabbrev--last-direction nil
614 dabbrev--last-expansion nil
615 dabbrev--last-expansion-location nil
616 dabbrev--friend-buffer-list nil
617 dabbrev--last-buffer nil
618 dabbrev--last-buffer-found nil
619 dabbrev--abbrev-char-regexp (or dabbrev-abbrev-char-regexp
620 "\\sw\\|\\s_")
621 dabbrev--check-other-buffers dabbrev-check-other-buffers))
622
623 ;;; Find all buffers that are considered "friends" according to the
624 ;;; function pointed out by dabbrev-friend-buffer-function.
625 (defun dabbrev--select-buffers ()
626 (save-excursion
627 (and (window-minibuffer-p (selected-window))
628 (set-buffer (dabbrev--minibuffer-origin)))
629 (let ((orig-buffer (current-buffer)))
630 (dabbrev-filter-elements
631 buffer (buffer-list)
632 (and (not (eq orig-buffer buffer))
633 (boundp 'dabbrev-friend-buffer-function)
634 (funcall dabbrev-friend-buffer-function buffer))))))
635
636 ;;; Search for ABBREV, N times, normally looking forward,
637 ;;; but looking in reverse instead if REVERSE is non-nil.
638 (defun dabbrev--try-find (abbrev reverse n ignore-case)
639 (save-excursion
640 (save-restriction
641 (widen)
642 (let ((expansion nil))
643 (and dabbrev--last-expansion-location
644 (goto-char dabbrev--last-expansion-location))
645 (let ((case-fold-search ignore-case)
646 (count n))
647 (while (and (> count 0)
648 (setq expansion (dabbrev--search abbrev
649 reverse
650 ignore-case)))
651 (setq count (1- count))))
652 (and expansion
653 (setq dabbrev--last-expansion-location (point)))
654 expansion))))
655
656 ;;; Find all expansions of ABBREV
657 (defun dabbrev--find-all-expansions (abbrev ignore-case)
658 (let ((all-expansions nil)
659 expansion)
660 (save-excursion
661 (goto-char (point-min))
662 (while (setq expansion (dabbrev--find-expansion abbrev -1 ignore-case))
663 (setq all-expansions (cons expansion all-expansions))))
664 all-expansions))
665
666 (defun dabbrev--scanning-message ()
667 (message "Scanning `%s'" (buffer-name (current-buffer))))
668
669 ;;; Find one occasion of ABBREV.
670 ;;; DIRECTION > 0 means look that many times backwards.
671 ;;; DIRECTION < 0 means look that many times forward.
672 ;;; DIRECTION = 0 means try both backward and forward.
673 ;;; IGNORE-CASE non-nil means ignore case when searching.
674 (defun dabbrev--find-expansion (abbrev direction ignore-case)
675 (let (expansion)
676 (save-excursion
677 (cond
678 (dabbrev--last-buffer
679 (set-buffer dabbrev--last-buffer)
680 (dabbrev--scanning-message))
681 ((and (not dabbrev-search-these-buffers-only)
682 (window-minibuffer-p (selected-window)))
683 (set-buffer (dabbrev--minibuffer-origin))
684 ;; In the minibuffer-origin buffer we will only search from
685 ;; the top and down.
686 ;; XEmacs: This is absolutely the stupidest thing I've ever
687 ;; heard of.
688 ;;(goto-char (point-min))
689 ;;(setq direction -1)
690 (dabbrev--scanning-message)))
691 (cond
692 ;; ------------------------------------------
693 ;; Look backwards
694 ;; ------------------------------------------
695 ((and (not dabbrev-search-these-buffers-only)
696 (>= direction 0)
697 (setq dabbrev--last-direction (min 1 direction))
698 (setq expansion (dabbrev--try-find abbrev t
699 (max 1 direction)
700 ignore-case)))
701 expansion)
702 ;; ------------------------------------------
703 ;; Look forward
704 ;; ------------------------------------------
705 ((and (or (not dabbrev-search-these-buffers-only)
706 dabbrev--last-buffer)
707 (<= direction 0)
708 (setq dabbrev--last-direction -1)
709 (setq expansion (dabbrev--try-find abbrev nil
710 (max 1 (- direction))
711 ignore-case)))
712 expansion)
713 ;; ------------------------------------------
714 ;; Look in other buffers.
715 ;; Start at (point-min) and look forward.
716 ;; ------------------------------------------
717 (t
718 (setq dabbrev--last-direction -1)
719 ;; Make sure that we should check other buffers
720 (or dabbrev--friend-buffer-list
721 dabbrev--last-buffer
722 (setq dabbrev--friend-buffer-list
723 (mapcar (function get-buffer)
724 dabbrev-search-these-buffers-only))
725 (not dabbrev--check-other-buffers)
726 (not (or (eq dabbrev--check-other-buffers t)
727 (progn
728 (setq dabbrev--check-other-buffers
729 (y-or-n-p "Scan other buffers also? ")))))
730 (let* (friend-buffer-list non-friend-buffer-list)
731 (setq dabbrev--friend-buffer-list
732 (funcall dabbrev-select-buffers-function))
733 (if dabbrev-check-all-buffers
734 (setq non-friend-buffer-list
735 (nreverse
736 (dabbrev-filter-elements
737 buffer (buffer-list)
738 (not (memq buffer dabbrev--friend-buffer-list))))
739 dabbrev--friend-buffer-list
740 (append dabbrev--friend-buffer-list
741 non-friend-buffer-list)))))
742 ;; Move buffers that are visible on the screen
743 ;; to the front of the list.
744 (if dabbrev--friend-buffer-list
745 (let ((w (next-window (selected-window))))
746 (while (not (eq w (selected-window)))
747 (setq dabbrev--friend-buffer-list
748 (cons (window-buffer w)
749 (delq (window-buffer w) dabbrev--friend-buffer-list)))
750 (setq w (next-window w)))))
751 ;; Walk through the buffers
752 (while (and (not expansion) dabbrev--friend-buffer-list)
753 (setq dabbrev--last-buffer
754 (car dabbrev--friend-buffer-list))
755 (setq dabbrev--friend-buffer-list
756 (cdr dabbrev--friend-buffer-list))
757 (set-buffer dabbrev--last-buffer)
758 (dabbrev--scanning-message)
759 (setq dabbrev--last-expansion-location (point-min))
760 (setq expansion (dabbrev--try-find abbrev nil 1 ignore-case)))
761 expansion)))))
762
763 (defun dabbrev--safe-replace-match (string &optional fixedcase literal)
764 (if (eq major-mode 'picture-mode)
765 (picture-replace-match string fixedcase literal)
766 (replace-match string fixedcase literal)))
767
768 ;;;----------------------------------------------------------------
769 ;;; Substitute the current string in buffer with the expansion
770 ;;; OLD is nil or the last expansion substring.
771 ;;; ABBREV is the abbreviation we are working with.
772 ;;; EXPANSION is the expansion substring.
773 (defun dabbrev--substitute-expansion (old abbrev expansion)
774 ;;(undo-boundary)
775 (let ((use-case-replace (and (eval dabbrev-case-fold-search)
776 (or (not dabbrev-upcase-means-case-search)
777 (string= abbrev (downcase abbrev)))
778 (eval dabbrev-case-replace))))
779 (and nil use-case-replace
780 (setq old (concat abbrev (or old "")))
781 (setq expansion (concat abbrev expansion)))
782 (if old
783 (save-excursion
784 (search-backward old))
785 ;;(store-match-data (list (point-marker) (point-marker)))
786 (search-backward abbrev))
787 ;; Make case of replacement conform to case of abbreviation
788 ;; provided (1) that kind of thing is enabled in this buffer
789 ;; and (2) the replacement itself is all lower case.
790 (dabbrev--safe-replace-match expansion
791 (not use-case-replace)
792 t)))
793
794
795 ;;;----------------------------------------------------------------
796 ;;; Search function used by dabbrevs library.
797
798 ;;; ABBREV is string to find as prefix of word. Second arg, REVERSE,
799 ;;; is t for reverse search, nil for forward. Variable dabbrev-limit
800 ;;; controls the maximum search region size. Third argment IGNORE-CASE
801 ;;; non-nil means treat case as insignificant while looking for a match
802 ;;; and when comparing with previous matches. Also if that's non-nil
803 ;;; and the match is found at the beginning of a sentence and is in
804 ;;; lower case except for the initial then it is converted to all lower
805 ;;; case for return.
806
807 ;;; Table of expansions already seen is examined in buffer
808 ;;; `dabbrev--last-table' so that only distinct possibilities are found
809 ;;; by dabbrev-re-expand.
810
811 ;;; Value is the expansion, or nil if not found.
812
813 (defun dabbrev--search (abbrev reverse ignore-case)
814 (save-match-data
815 (let ((pattern1 (concat (regexp-quote abbrev)
816 "\\(" dabbrev--abbrev-char-regexp "\\)"))
817 (pattern2 (concat (regexp-quote abbrev)
818 "\\(\\(" dabbrev--abbrev-char-regexp "\\)+\\)"))
819 (found-string nil))
820 ;; Limited search.
821 (save-restriction
822 (and dabbrev-limit
823 (narrow-to-region dabbrev--last-expansion-location
824 (+ (point)
825 (if reverse (- dabbrev-limit) dabbrev-limit))))
826 ;;--------------------------------
827 ;; Look for a distinct expansion, using dabbrev--last-table.
828 ;;--------------------------------
829 (while (and (not found-string)
830 (if reverse
831 (re-search-backward pattern1 nil t)
832 (re-search-forward pattern1 nil t)))
833 (goto-char (match-beginning 0))
834 ;; In case we matched in the middle of a word,
835 ;; back up to start of word and verify we still match.
836 (dabbrev--goto-start-of-abbrev)
837
838 (if (not (looking-at pattern1))
839 nil
840 ;; We have a truly valid match. Find the end.
841 (re-search-forward pattern2)
842 (setq found-string
843 (buffer-substring (match-beginning 1) (match-end 1)))
844 (and ignore-case (setq found-string (downcase found-string)))
845 ;; Ignore this match if it's already in the table.
846 (if (dabbrev-filter-elements
847 table-string dabbrev--last-table
848 (string= found-string table-string))
849 (setq found-string nil)))
850 ;; Prepare to continue searching.
851 (if reverse
852 (goto-char (match-beginning 0))
853 (goto-char (match-end 0))))
854 ;; If we found something, use it.
855 (if found-string
856 ;; Put it into `dabbrev--last-table'
857 ;; and return it (either downcased, or as is).
858 (let ((result
859 (buffer-substring (match-beginning 0) (match-end 0))))
860 (setq dabbrev--last-table
861 (cons found-string dabbrev--last-table))
862 (if (and ignore-case (eval dabbrev-case-replace))
863 (downcase result)
864 result)))))))
865
866 (provide 'dabbrev)
867
868 ;; dabbrev.el ends here
869
870