comparison lisp/dabbrev.el @ 10230:7396503653cf

Complete rewrite by Lars.Lindberg@sypro.cap.se.
author Richard M. Stallman <rms@gnu.org>
date Sat, 24 Dec 1994 01:26:51 +0000
parents 069c54e77fd1
children e88ff4b30786
comparison
equal deleted inserted replaced
10229:634f36d4b2ae 10230:7396503653cf
1 ;;; dabbrev.el --- dynamic abbreviation package for GNU Emacs. 1 ;;; new-dabbrev.el --- dynamic abbreviation package
2
3 ;; Copyright (C) 1985, 1986 Free Software Foundation, Inc. 2 ;; Copyright (C) 1985, 1986 Free Software Foundation, Inc.
4 3
5 ;; Last-Modified: 16 Mar 1992 4 ;; Author: Don Morrison
6 ;; Copyright (C) 1985, 1986 Free Software Foundation, Inc. 5 ;; Maintainer: Lars Lindberg <Lars.Lindberg@sypro.cap.se>
7 6 ;; Created: 16 Mars 1992
8 ;; Maintainer: FSF 7 ;; Version: 4.4.2 beta
9 ;; Keywords: abbrev 8 (defun dabbrev--version () "4.4.2 beta")
10 9 ;; Keywords: abbrev expand completion
11 ;; This file is part of GNU Emacs. 10
12 11 ;; This program is free software; you can redistribute it and/or modify
13 ;; GNU Emacs is free software; you can redistribute it and/or modify
14 ;; it under the terms of the GNU General Public License as published by 12 ;; it under the terms of the GNU General Public License as published by
15 ;; the Free Software Foundation; either version 2, or (at your option) 13 ;; the Free Software Foundation; either version 2 of the License, or
16 ;; any later version. 14 ;; (at your option) any later version.
17 15 ;;
18 ;; GNU Emacs is distributed in the hope that it will be useful, 16 ;; This program is distributed in the hope that it will be useful,
19 ;; but WITHOUT ANY WARRANTY; without even the implied warranty of 17 ;; but WITHOUT ANY WARRANTY; without even the implied warranty of
20 ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 18 ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
21 ;; GNU General Public License for more details. 19 ;; GNU General Public License for more details.
22 20 ;;
23 ;; You should have received a copy of the GNU General Public License 21 ;; You should have received a copy of the GNU General Public License
24 ;; along with GNU Emacs; see the file COPYING. If not, write to 22 ;; along with this program; if not, write to the Free Software
25 ;; the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. 23 ;; Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
26 24
27 ;;; Commentary: 25 ;;; Commentary:
28 26
29 ; DABBREVS - "Dynamic abbreviations" hack, originally written by Don Morrison 27 ;; The purpose with this package is to let you write just a few
30 ; for Twenex Emacs. Converted to mlisp by Russ Fish. Supports the table 28 ;; characters of words you've written earlier to be able to expand
31 ; feature to avoid hitting the same expansion on re-expand, and the search 29 ;; them.
32 ; size limit variable. Bugs fixed from the Twenex version are flagged by 30 ;;
33 ; comments starting with ;;; . 31 ;; To expand a word, just put the point right after the word and press
34 ; 32 ;; M-/ (dabbrev-expand) or M-C-/ (dabbrev-completion).
35 ; converted to Emacs Lisp by Spencer Thomas. 33 ;;
36 ; Thoroughly cleaned up by Richard Stallman. 34 ;; There are powerful things in this package that aren't turned on by
37 ; 35 ;; default. I recommend you to do the following.
38 ; If anyone feels like hacking at it, Bob Keller (Keller@Utah-20) first 36 ;;
39 ; suggested the beast, and has some good ideas for its improvement, but 37 ;; Put the following 2 lines in your .emacs file:
40 ; doesn't know TECO (the lucky devil...). One thing that should definitely 38 ;; (setq dabbrev-always-check-other-buffers t)
41 ; be done is adding the ability to search some other buffer(s) if you can?t 39 ;; (setq dabbrev-abbrev-char-regexp "\\sw\\|\\s_")
42 ; find the expansion you want in the current one. 40 ;;
41 ;; Dabbrev will now search in all buffers with the same major mode for
42 ;; your expansions. It will also search for complete symbols, the old
43 ;; dabbrev package only looked half-heartedly for symbols.
44 ;;
45 ;; Check out the customizable variables below to learn about all the
46 ;; features of this package.
47
48 ;;; Hints and tips for major modes writers:
49
50 ;; Recommended values C/Lisp etc text
51 ;; dabbrev-case-fold-search nil t
52 ;; dabbrev-case-replace nil t
53 ;;
54 ;; Set the variables you want special for your mode like this:
55 ;; (set (make-local-variable 'dabbrev-case-replace) nil)
56 ;; Then you don't interfer with other modes.
57 ;;
58 ;; If your mode handles buffers that refers to other buffers
59 ;; (i.e. compilation-mode, gud-mode), then try to set
60 ;; `dabbrev-select-buffers-function' or `dabbrev-friend-buffer-function'
61 ;; to a function that point out those buffers.
62
63 ;; Same goes for major-modes that are connected to other modes. There
64 ;; are for instance a number of mail-modes. One for reading, one for
65 ;; creating a new mail etc. Maybe those should be connected.
66
67 ;; Example for GNUS (when we write a reply, we want dabbrev to look in
68 ;; the article for expansion):
69 ;; (set (make-local-variable 'dabbrev-friend-buffer-function)
70 ;; (lambda (buffer)
71 ;; (save-excursion
72 ;; (set-buffer buffer)
73 ;; (memq major-mode '(news-reply-mode gnus-article-mode)))))
74
75 ;;; Change Log
76 ;; 4.4.2 1994-11-25
77 ;; Added new variable; `dabbrev-check-rest-of-buffers'.
78 ;; 4.4.1 1994-11-24
79 ;; Now has correct creation date.
80 ;; Added kill-ring idea to "Future enhancements".
81 ;; 4.4 1994-11-22
82 ;; Changed the copyright text. Thanks [hymie].
83 ;; new-dabbrev now handles abbrevs that starts with symbol
84 ;; syntax. Thanks [burgett] for kicking me to do it.
85 ;; Now also handles `dabbrev-abbrev-skip-leading-regexp' better.
86 ;; 4.3 1994-11-14
87 ;; Now only displays "Expansion found in <buffer>" when the
88 ;; expansion has been found in buffers that hasn't been examined
89 ;; yet. Thanks [kifer].
90 ;; Added a new variable; `dabbrev-search-these-buffers-only'.
91 ;; Fixed bug in mini-buffer completion. Thanks [kifer].
92 ;; Found a real time-waster when using `dabbrev-completion'.
93 ;; Thanks for the elp.el package Barry Warsaw!
94 ;; Found bug that made point move in other buffers.
95 ;; Now handles Lucid emacs define-key style.
96 ;; Thanks [jules].
97 ;; Now uses the `<symbol>' syntax in doc strings.
98 ;; Cosmetic bugs and syntactical bugs in documentation strings.
99 ;; Thanks [hawley].
100 ;; 4.2 1994-03-04
101 ;; Now searches other buffers again. Thanks [ake].
102 ;; 4.1 1994-02-22
103 ;; Introduced the new variables `dabbrev-case-fold-search' and
104 ;; `dabbrev-case-replace'.
105 ;; Introduced the new variable `dabbrev-select-buffers-function'.
106 ;; Introduced the new variable `dabbrev-abbrev-skip-leading-regexp'.
107 ;; Changed `dabbrev-always-check-other-buffers' to not buffer local.
108 ;; Thanks [kifer].
109 ;; Bug in `dabbrev-completion', x expanded to xxy instead of xy.
110 ;; Thanks [kifer].
111 ;; Added `dabbrev-submit-feedback' for better error-reporting.
112 ;; The hooks (`dabbrev-select-buffers-function' and
113 ;; `dabbrev-friend-buffer-function') are now not automatically
114 ;; buffer local.
115 ;; Now provides dabbrev too.
116 ;; 3.2 1993-12-14
117 ;; Message for expansion found in buffer other than current.
118 ;; Minor bugs.
119 ;; 3.1 1993-12-13
120 ;; Better comment for `dabbrev-abbrev-char-regexp'.
121 ;; 3.0 1993-12-09
122 ;; Freshed things up for the release.
123 ;; `dabbrev-completion' now doesn't have to use the minibuffer.
124 ;; Thanks [alon].
125 ;; 2.0 1993-12-02 Lars Lindberg <lli@sypro.cap.se>
126 ;; Searches in other buffers for the expansion.
127 ;; Works also for the minibuffer.
128 ;; Added `dabbrev-completion'.
129 ;; More efficient code.
130 ;; Found minor bugs.
131 ;; 1.0 converted to Emacs Lisp by Spencer Thomas.
132 ;; Thoroughly cleaned up by Richard Stallman.
133 ;; 0.0
134 ;; DABBREVS - "Dynamic abbreviations" hack, originally written by
135 ;; Don Morrison for Twenex Emacs. Converted to mlisp by Russ Fish.
136 ;; Supports the table feature to avoid hitting the same expansion on
137 ;; re-expand, and the search size limit variable.
138
139 ;; Known bugs and limitations.
140 ;; - Possible to do several levels of `dabbrev-completion' in the
141 ;; minibuffer.
142 ;; - dabbrev-completion doesn't handle resetting the globals variables
143 ;; right. It resets them after finding the abbrev.
144
145 ;; Future enhancements
146 ;; - Check the tags-files? Like tags-complete?
147 ;; - Add the possibility of searching both forward and backward to
148 ;; the nearest expansion.
149 ;; - Check the kill-ring when everything else fails. (Maybe something
150 ;; for hippie-expand?). [Bng] <boris@cs.rochester.edu>
151
152 ;;; Thanks goes to
153 ;; [hymie] Hyman Rosen <marks!hymie@jyacc.jyacc.com>
154 ;; [burgett] Steve Burgett <burgett@bizet.eecs.berkeley.edu>
155 ;; [jules] Julian Gosnell <jules@x.co.uk>
156 ;; [kifer] Michael Kifer <kifer@sbcs.sunysb.edu>
157 ;; [ake] Ake Stenhoff <extaksf@aom.ericsson.se>
158 ;; [alon] Alon Albert <al%imercury@uunet.uu.net>
159 ;; [tromey] Tom Tromey <tromey@busco.lanl.gov>
160 ;; [Rolf] Rolf Schreiber <rolf@mathematik.uni-stuttgart.de>
161 ;; [Petri] Petri Raitio <per@tekla.fi>
162 ;; [ejb] Jay Berkenbilt <ejb@ERA.COM>
163 ;; [hawley] Bob Hawley <rth1@quartet.mt.att.com>
164 ;; ... and to all the people who have participated in the beta tests.
43 165
44 ;;; Code: 166 ;;; Code:
45 167 (require 'cl)
46 ;; (defun dabbrevs-help () 168
47 ;; "Give help about dabbrevs." 169 ;;;----------------------------------------------------------------
48 ;; (interactive) 170 ;;;----------------------------------------------------------------
49 ;; (&info "emacs" "dabbrevs") ; Select the specific info node. 171 ;;; Customization variables
50 ;; ) 172 ;;;----------------------------------------------------------------
51 (defvar dabbrevs-limit nil 173 ;;;----------------------------------------------------------------
52 "*Limits region searched by `dabbrevs-expand' to this many chars away.") 174 (defvar dabbrev-backward-only nil
53 (make-variable-buffer-local 'dabbrevs-limit) 175 "*If non-NIL, `dabbrev-expand' only looks backwards.")
54 176
55 (defvar dabbrevs-backward-only nil 177 (defvar dabbrev-limit nil
56 "*If non-NIL, `dabbrevs-expand' only looks backwards.") 178 "*Limits region searched by `dabbrev-expand' to this many chars away.")
57 179
58 ; State vars for dabbrevs-re-expand. 180 (defvar dabbrev-abbrev-skip-leading-regexp nil
59 (defvar last-dabbrevs-table nil 181 "*Regexp for skipping leading characters of an abbreviation.
60 "Table of expansions seen so far (local)") 182
61 (make-variable-buffer-local 'last-dabbrevs-table) 183 Example: Set this to \"\\\\$\" for programming languages that sometimes
62 184 has and sometimes has not a leading $ for variable names.
63 (defvar last-dabbrevs-abbreviation "" 185
64 "Last string we tried to expand (local).") 186 Set this to nil if no characters should be skipped.")
65 (make-variable-buffer-local 'last-dabbrevs-abbreviation) 187
66 188 ;; I recommend that you set this to nil.
67 (defvar last-dabbrevs-direction 0 189 (defvar dabbrev-case-fold-search 'case-fold-search
68 "Direction of last dabbrevs search (local)") 190 "*T if dabbrev searches should ignore case.
69 (make-variable-buffer-local 'last-dabbrevs-direction) 191 nil if case is significant.
70 192 Non-nil and not t means evaluate for value.
71 (defvar last-dabbrevs-abbrev-location nil 193
72 "Location last abbreviation began (local).") 194 Example:Setting this to 'case-fold-search means evaluate that variable
73 (make-variable-buffer-local 'last-dabbrevs-abbrev-location) 195 to see if it is t or non-nil.")
74 196
75 (defvar last-dabbrevs-expansion nil 197 (defvar dabbrev-upcase-means-case-search nil
76 "Last expansion of an abbreviation. (local)") 198 "*The significance of an uppercase character in an abbreviation.
77 (make-variable-buffer-local 'last-dabbrevs-expansion) 199
78 200 This variable only makes sense when the value of
79 (defvar last-dabbrevs-expansion-location nil 201 `dabbrev-case-fold-search' evaluates to t.
80 "Location the last expansion was found. (local)") 202
81 (make-variable-buffer-local 'last-dabbrevs-expansion-location) 203 nil = case fold search
204 t = case sensitive search")
205
206 ;; I recommend that you set this to nil.
207 (defvar dabbrev-case-replace 'case-replace
208 "*T if dabbrev should preserve case when expanding the abbreviation.
209 nil if it should not.
210 Non-nil and not t means evaluate for value.
211
212 This variable only makes sense when the value of
213 `dabbrev-case-fold-search' evaluates to t.
214
215 Example: Setting this to 'case-replace means evaluate that variable to
216 see if it is t or non-nil.")
217
218 ;; I recommend that you set this to "\\sw\\|\\s_"
219 (defvar dabbrev-abbrev-char-regexp nil
220 "*A regexp that recognizes a character in an abbreviation or an
221 expansion. Will be surrounded with \\\\( ... \\\\) when used.
222
223 Set this to \"\\\\sw\" if you want ordinary words or
224 \"\\\\sw\\\\|\\\\s_\" if you want symbols.
225
226 You can also set it to nil if you want old-style dabbrev searching
227 (the abbreviation is from point to previous word-start, the
228 search is for symbols).
229
230 For instance, if you are programming in Lisp, yes-or-no-p is a symbol,
231 while 'yes', 'or', 'no' and 'p' are considered words. If you set this
232 variable to nil, then expanding yes-or-no- will look for a symbol
233 starting with or containing 'no-'. If you set this variable to
234 \"\\\\sw\\\\|\\\\s_\" dabbrev will look for a symbol starting with
235 \"yes-or-no-\". Finally, if you set this variable to \"\\\\sw\", then an
236 error will be signalled, because \"-\" is not part of a word but if you
237 try to expand \"yes-or-no\", dabbrev will look for a word starting with
238 \"no\".
239
240 The recommended value is \"\\\\sw\\\\|\\\\s_\".")
241
242 ;; I recommend that you set this to t.
243 (defvar dabbrev-check-rest-of-buffers nil
244 "*Should dabbrev package search in all buffers?.
245
246 Non-nil means first look in buffers pointed out by
247 `dabbrev-select-buffers-function' and then look in the rest of the
248 buffers.")
249
250
251 ;; I recommend that you set this to t.
252 (defvar dabbrev-always-check-other-buffers nil
253 "*Should \\[dabbrev-expand] look in other buffers?\
254 nil = Don't look in other buffers.\n\
255 t = Look in other buffers.\n\
256 Value other than nil and t = ask the user if he want's to look in
257 other buffers.
258
259 The recommended value is t.")
260
261 ;; I guess setting this to a function that selects all C- or C++-
262 ;; mode buffers would be a good choice for a debugging buffer,
263 ;; when debugging C- or C++-code.
264 (defvar dabbrev-select-buffers-function 'dabbrev--select-buffers
265 "A function that selects buffers that should be searched by dabbrev.
266
267 The function should take no arguments and return a list of buffers to
268 search for expansions. Have a look at `dabbrev--select-buffers' for
269 an example.
270
271 A mode setting this variable should make it buffer local.")
272
273 (defvar dabbrev-friend-buffer-function 'dabbrev--same-major-mode-p
274 "*A function to check if OTHER-BUFFER should be searched by dabbrev.
275
276 The function should take one argument, OTHER-BUFFER, and return
277 non-nil if that buffer should be searched. Have a look at
278 `dabbrev--same-major-mode-p' for an example.
279
280 Setting this makes sense only if the function pointed out by
281 `dabbrev-select-buffers-function' uses it. The package function
282 `dabbrev--select-buffers' is such a function.
283
284 A mode setting this variable should make it buffer local.")
285
286 (defvar dabbrev-search-these-buffers-only nil
287 "Should be a list of buffers if non-nil.
288
289 Dabbrev search will only look in these buffers. It will not even look
290 in the current buffer if it is not a member of this list.")
291
292 ;;;----------------------------------------------------------------
293 ;;;----------------------------------------------------------------
294 ;;; Internal variables
295 ;;;----------------------------------------------------------------
296 ;;;----------------------------------------------------------------
297
298 ;; Last obarray of completions in `dabbrev-completion'
299 (defvar dabbrev--last-obarray nil)
300
301 ;; Table of expansions seen so far
302 (defvar dabbrev--last-table nil)
303
304 ;; Last string we tried to expand.
305 (defvar dabbrev--last-abbreviation nil)
306
307 ;; Location last abbreviation began
308 (defvar dabbrev--last-abbrev-location nil)
309
310 ;; Direction of last dabbrevs search
311 (defvar dabbrev--last-direction 0)
312
313 ;; Last expansion of an abbreviation.
314 (defvar dabbrev--last-expansion nil)
315
316 ;; Location the last expansion was found.
317 (defvar dabbrev--last-expansion-location nil)
318
319 ;; The list of remaining buffers with the same mode as current buffer.
320 (defvar dabbrev--friend-buffer-list nil)
321
322 ;; The buffer we looked in last.
323 (defvar dabbrev--last-buffer nil)
324
325 ;; The buffer we found the expansion last time.
326 (defvar dabbrev--last-buffer-found nil)
327
328 ;; The buffer we last did a completion in.
329 (defvar dabbrev--last-completion-buffer nil)
330
331 ;; Same as dabbrev-always-check-other-buffers, but is set for every expand.
332 (defvar dabbrev--check-other-buffers dabbrev-always-check-other-buffers)
333
334 ;; The regexp for recognizing a character in an abbreviation.
335 (defvar dabbrev--abbrev-char-regexp nil)
336
337 ;;;----------------------------------------------------------------
338 ;;;----------------------------------------------------------------
339 ;;; Macros
340 ;;;----------------------------------------------------------------
341 ;;;----------------------------------------------------------------
342
343 ;;; Get the buffer that mini-buffer was activated from
344 (defsubst dabbrev--minibuffer-origin ()
345 (car (cdr (buffer-list))))
346
347 ;;;----------------------------------------------------------------
348 ;;;----------------------------------------------------------------
349 ;;; Exported functions
350 ;;;----------------------------------------------------------------
351 ;;;----------------------------------------------------------------
352
353 ;;;###autoload
354 (define-key esc-map "/" 'dabbrev-expand)
355 ;;;###autoload
356 (if (string-match "Lucid$" emacs-version)
357 (define-key esc-map [(control /)] 'dabbrev-completion)
358 (define-key esc-map [?\C-/] 'dabbrev-completion))
359
360 ;;;###autoload
361 (defun dabbrev-completion (&optional arg)
362 "Completion on current word.
363
364 Like \\[dabbrev-expand] but finds all expansions in the current buffer
365 and presents suggestions for completion.
366
367 If you call this function with prefix ARG, then it searches all
368 buffers accepted by the function pointed out by
369 `dabbrev-friend-buffer-function' to find the completions.
370
371 With no prefix ARG it tries to reuse the old completion list
372 before making a new one."
373
374 (interactive "*P")
375 (let* ((dabbrev-always-check-other-buffers (and arg t))
376 (abbrev (dabbrev--abbrev-at-point))
377 (ignore-case-p (and (eval dabbrev-case-fold-search)
378 (or (not dabbrev-upcase-means-case-search)
379 (string= abbrev (downcase abbrev)))))
380 (my-obarray dabbrev--last-obarray)
381 init)
382 (save-excursion
383 (if (and (null arg)
384 my-obarray
385 (or (eq dabbrev--last-completion-buffer (current-buffer))
386 (and (window-minibuffer-p (selected-window))
387 (eq dabbrev--last-completion-buffer
388 (dabbrev--minibuffer-origin))))
389 dabbrev--last-abbreviation
390 (>= (length abbrev) (length dabbrev--last-abbreviation))
391 (string= dabbrev--last-abbreviation
392 (substring abbrev 0
393 (length dabbrev--last-abbreviation)))
394 (setq init (try-completion abbrev my-obarray)))
395 ;;--------------------------------
396 ;; This is a continue.
397 ;;--------------------------------
398 (progn)
399 ;;--------------------------------
400 ;; New abbreviation to expand.
401 ;;--------------------------------
402 (dabbrev--reset-global-variables)
403 (setq dabbrev--last-abbreviation abbrev)
404 ;; Find all expansion
405 (let ((completion-list
406 (dabbrev--find-all-expansions abbrev ignore-case-p)))
407 ;; Make an obarray with all expansions
408 (setq my-obarray (make-vector (length completion-list) 0))
409 (or (> (length my-obarray) 0)
410 (error "No dynamic expansion for \"%s\" found%s."
411 abbrev
412 (if dabbrev--check-other-buffers "" " in this-buffer")))
413 (cond
414 ((or (not ignore-case-p)
415 (not dabbrev-case-replace))
416 (mapc (function (lambda (string)
417 (intern string my-obarray)))
418 completion-list))
419 ((string= abbrev (upcase abbrev))
420 (mapc (function (lambda (string)
421 (intern (upcase string) my-obarray)))
422 completion-list))
423 ((string= (substring abbrev 0 1)
424 (upcase (substring abbrev 0 1)))
425 (mapc (function (lambda (string)
426 (intern (dabbrev--capitalize string) my-obarray)))
427 completion-list))
428 (t
429 (mapc (function (lambda (string)
430 (intern (downcase string) my-obarray)))
431 completion-list)))
432 (setq dabbrev--last-obarray my-obarray)
433 (setq dabbrev--last-completion-buffer (current-buffer))
434 ;; Find the longest common string.
435 (setq init (try-completion abbrev my-obarray)))))
436 ;;--------------------------------
437 ;; Let the user choose between the expansions
438 ;;--------------------------------
439 (or (stringp init)
440 (setq init abbrev))
441 (cond
442 ;; * Replace string fragment with matched common substring completion.
443 ((and (not (string-equal init ""))
444 (not (string-equal (downcase init) (downcase abbrev))))
445 (if (> (length (all-completions init my-obarray)) 1)
446 (message "Repeat '%s' to see all completions" this-command)
447 (message "The only possible completion"))
448 (dabbrev--substitute-expansion nil abbrev init))
449 (t
450 ;; * String is a common substring completion already. Make list.
451 (message "Making completion list...")
452 (with-output-to-temp-buffer " *Completions*"
453 (display-completion-list (all-completions init my-obarray)))
454 (message "Making completion list... Done.")))
455 (and (window-minibuffer-p (selected-window))
456 (message nil))))
82 457
83 ;;;###autoload 458 ;;;###autoload
84 (defun dabbrev-expand (arg) 459 (defun dabbrev-expand (arg)
85 "Expand previous word \"dynamically\". 460 "Expand previous word \"dynamically\".
461
86 Expands to the most recent, preceding word for which this is a prefix. 462 Expands to the most recent, preceding word for which this is a prefix.
87 If no suitable preceding word is found, words following point are considered. 463 If no suitable preceding word is found, words following point are
88 464 considered. If still no suitable word is found, then look in the
89 If `case-fold-search' and `case-replace' are non-nil (usually true) 465 buffers accepted by the function pointed out by variable
90 then the substituted word may be case-adjusted to match the abbreviation 466 `dabbrev-friend-buffer-function'.
91 that you had typed. This takes place if the substituted word, as found, 467
92 is all lower case, or if it is at the beginning of a sentence and only 468 A positive prefix argument, N, says to take the Nth backward _distinct_
93 its first letter was upper case. 469 possibility. A negative argument says search forward.
94
95 A positive prefix arg N says to take the Nth backward DISTINCT
96 possibility. A negative argument says search forward. The variable
97 `dabbrev-backward-only' may be used to limit the direction of search to
98 backward if set non-nil.
99 470
100 If the cursor has not moved from the end of the previous expansion and 471 If the cursor has not moved from the end of the previous expansion and
101 no argument is given, replace the previously-made expansion 472 no argument is given, replace the previously-made expansion
102 with the next possible expansion not yet tried." 473 with the next possible expansion not yet tried.
474
475 The variable `dabbrev-backward-only' may be used to limit the
476 direction of search to backward if set non-nil.
477
478 To make it more powerful, make sure that
479 `dabbrev-always-check-other-buffers' is set to t.
480
481 Also check out `dabbrev-abbrev-char-regexp' and \\[dabbrev-completion]."
103 (interactive "*P") 482 (interactive "*P")
104 (let (abbrev expansion old which loc n pattern 483 (let (abbrev expansion old direction)
105 (do-case (and case-fold-search case-replace)))
106 ;; abbrev -- the abbrev to expand 484 ;; abbrev -- the abbrev to expand
107 ;; expansion -- the expansion found (eventually) or nil until then 485 ;; expansion -- the expansion found (eventually) or nil until then
108 ;; old -- the text currently in the buffer 486 ;; old -- the text currently in the buffer
109 ;; (the abbrev, or the previously-made expansion) 487 ;; (the abbrev, or the previously-made expansion)
110 ;; loc -- place where expansion is found
111 ;; (to start search there for next expansion if requested later)
112 ;; do-case -- non-nil if should transform case when substituting.
113 (save-excursion 488 (save-excursion
114 (if (and (null arg) 489 (if (and (null arg)
115 (eq last-command this-command) 490 dabbrev--last-abbrev-location
116 last-dabbrevs-abbrev-location) 491 (or (eq last-command this-command)
492 (and (window-minibuffer-p (selected-window))
493 (= dabbrev--last-abbrev-location
494 (point)))))
495 ;;--------------------------------
496 ;; This is a redo.
497 ;;--------------------------------
117 (progn 498 (progn
118 (setq abbrev last-dabbrevs-abbreviation) 499 (setq abbrev dabbrev--last-abbreviation)
119 (setq old last-dabbrevs-expansion) 500 (setq old dabbrev--last-expansion)
120 (setq which last-dabbrevs-direction)) 501 (setq direction dabbrev--last-direction))
121 (setq which (if (null arg) 502 ;;--------------------------------
122 (if dabbrevs-backward-only 1 0) 503 ;; New abbreviation to expand.
123 (prefix-numeric-value arg))) 504 ;;--------------------------------
124 (setq loc (point)) 505 (dabbrev--reset-global-variables)
125 (forward-word -1) 506 (setq direction (if (null arg)
126 (setq last-dabbrevs-abbrev-location (point)) ; Original location. 507 (if dabbrev-backward-only 1 0)
127 (setq abbrev (buffer-substring (point) loc)) 508 (prefix-numeric-value arg)))
128 (setq old abbrev) 509 (setq abbrev (dabbrev--abbrev-at-point))
129 (setq last-dabbrevs-expansion-location nil) 510 (setq old nil))
130 (setq last-dabbrev-table nil)) ; Clear table of things seen. 511
131 512 ;;--------------------------------
132 (setq pattern (concat "\\b" (regexp-quote abbrev) "\\(\\sw\\|\\s_\\)+")) 513 ;; Find the expansion
133 ;; Try looking backward unless inhibited. 514 ;;--------------------------------
134 (if (>= which 0) 515 (setq expansion
135 (progn 516 (dabbrev--find-expansion abbrev direction
136 (setq n (max 1 which)) 517 (and (eval dabbrev-case-fold-search)
137 (if last-dabbrevs-expansion-location 518 (or (not dabbrev-upcase-means-case-search)
138 (goto-char last-dabbrevs-expansion-location)) 519 (string= abbrev (downcase abbrev)))))))
139 (while (and (> n 0) 520 (cond
140 (setq expansion (dabbrevs-search pattern t do-case))) 521 ((not expansion)
141 (setq loc (point-marker)) 522 (dabbrev--reset-global-variables)
142 (setq last-dabbrev-table (cons expansion last-dabbrev-table)) 523 (if old
143 (setq n (1- n))) 524 (save-excursion
144 (or expansion 525 (search-backward (substring old (length abbrev)))
145 (setq last-dabbrevs-expansion-location nil)) 526 (delete-region (match-beginning 0) (match-end 0))))
146 (setq last-dabbrevs-direction (min 1 which)))) 527 (error "No%s dynamic expansion for \"%s\" found."
147 528 (if old " further" "") abbrev))
148 (if (and (<= which 0) (not expansion)) ; Then look forward. 529 (t
149 (progn 530 (if (not (eq dabbrev--last-buffer dabbrev--last-buffer-found))
150 (setq n (max 1 (- which))) 531 (progn
151 (if last-dabbrevs-expansion-location 532 (message "Expansion found in '%s'"
152 (goto-char last-dabbrevs-expansion-location)) 533 (buffer-name dabbrev--last-buffer))
153 (while (and (> n 0) 534 (setq dabbrev--last-buffer-found dabbrev--last-buffer))
154 (setq expansion (dabbrevs-search pattern nil do-case))) 535 (message nil))
155 (setq loc (point-marker))
156 (setq last-dabbrev-table (cons expansion last-dabbrev-table))
157 (setq n (1- n)))
158 (setq last-dabbrevs-direction -1))))
159
160 (if (not expansion)
161 (let ((first (string= abbrev old)))
162 (setq last-dabbrevs-abbrev-location nil)
163 (if (not first)
164 (progn (undo-boundary)
165 (search-backward old)
166 (if (eq major-mode 'picture-mode)
167 (picture-replace-match abbrev t 'literal)
168 (replace-match abbrev t 'literal))))
169 (error (if first
170 "No dynamic expansion for \"%s\" found."
171 "No further dynamic expansions for \"%s\" found.")
172 abbrev))
173 ;; Success: stick it in and return. 536 ;; Success: stick it in and return.
174 (undo-boundary) 537 (dabbrev--substitute-expansion old abbrev expansion)
175 (search-backward old) 538 ;; Save state for re-expand.
176 ;; Make case of replacement conform to case of abbreviation 539 (setq dabbrev--last-expansion expansion)
177 ;; provided (1) that kind of thing is enabled in this buffer 540 (setq dabbrev--last-abbreviation abbrev)
178 ;; and (2) the replacement itself is all lower case. 541 (setq dabbrev--last-abbrev-location (point-marker))))))
179 ;; First put back the original abbreviation with its original 542
180 ;; case pattern. 543 (eval-when-compile (require 'reporter))
181 (save-excursion 544 (defun dabbrev-submit-feedback ()
182 (if (eq major-mode 'picture-mode) 545 "Submit via mail a bug report on the dabbrev package."
183 (picture-replace-match abbrev t 'literal) 546 (interactive)
184 (replace-match abbrev t 'literal))) 547 (require 'reporter)
185 (search-forward abbrev) 548 (and (y-or-n-p "Do you really want to submit a report on the dabbrev package? ")
186 (let ((do-case (and do-case 549 (reporter-submit-bug-report
187 (string= (substring expansion 1) 550 "Lars Lindberg <lli@sypro.cap.se>"
188 (downcase (substring expansion 1)))))) 551 (format "new-dabbrev.el (Release %s)" (dabbrev--version))
189 ;; First put back the original abbreviation with its original 552 '(dabbrev-backward-only
190 ;; case pattern. 553 dabbrev-limit
554 dabbrev-case-fold-search
555 dabbrev-case-replace
556 dabbrev-upcase-means-case-search
557 dabbrev-abbrev-char-regexp
558 dabbrev-always-check-other-buffers
559 dabbrev-select-buffers-function
560 dabbrev-friend-buffer-function)
561 nil nil nil)))
562
563 ;;;----------------------------------------------------------------
564 ;;;----------------------------------------------------------------
565 ;;; Local functions
566 ;;;----------------------------------------------------------------
567 ;;;----------------------------------------------------------------
568
569 (defun dabbrev--capitalize (string)
570 ;; Capitalize STRING (See capitalize-word)
571 (let ((new-string ""))
572 (save-match-data
573 (while (string-match "\\w+" string)
574 (let* ((mb (match-beginning 0))
575 (me (match-end 0))
576 (ms (substring string mb me)))
577 (setq new-string
578 (concat new-string
579 (substring string 0 mb)
580 (upcase (substring ms 0 1))
581 (downcase (substring ms 1))))
582 (setq string (substring string me)))))
583 new-string))
584
585 ;;; Checks if OTHER-BUFFER has the same major mode as current buffer.
586 (defun dabbrev--same-major-mode-p (other-buffer)
587 (let ((orig-mode major-mode))
588 (save-excursion
589 (set-buffer other-buffer)
590 (eq orig-mode major-mode))))
591
592 ;;; Back over all abbrev type characters and then moves forward over
593 ;;; all skip characters.
594 (defun dabbrev--goto-start-of-abbrev ()
595 ;; Move backwards over abbrev chars
596 (save-match-data
597 (when (not (bobp))
598 (forward-char -1)
599 (while (and (looking-at dabbrev--abbrev-char-regexp)
600 (not (bobp)))
601 (forward-char -1))
602 (or (looking-at dabbrev--abbrev-char-regexp)
603 (forward-char 1)))
604 (and dabbrev-abbrev-skip-leading-regexp
605 (while (looking-at dabbrev-abbrev-skip-leading-regexp)
606 (forward-char 1)))))
607
608 ;;; Extract the symbol at point to serve as abbrevitation.
609 (defun dabbrev--abbrev-at-point ()
610 ;; Check for error
611 (save-excursion
612 (save-match-data
613 (if (or (bobp)
614 (progn
615 (forward-char -1)
616 (not (looking-at (concat "\\("
617 (or dabbrev-abbrev-char-regexp
618 "\\sw\\|\\s_")
619 "\\)+")))))
620 (error "Not positioned immediately after an abbreviation."))))
621 ;; Return abbrev at point
622 (save-excursion
623 (setq dabbrev--last-abbrev-location (point))
624 (buffer-substring (point)
625 (progn (dabbrev--goto-start-of-abbrev)
626 (point)))))
627
628 ;;; Initializes all global variables
629 (defun dabbrev--reset-global-variables ()
630 ;; dabbrev--last-obarray and dabbrev--last-completion-buffer
631 ;; must not be reset here.
632 (setq dabbrev--last-table nil
633 dabbrev--last-abbreviation nil
634 dabbrev--last-abbrev-location nil
635 dabbrev--last-direction nil
636 dabbrev--last-expansion nil
637 dabbrev--last-expansion-location nil
638 dabbrev--friend-buffer-list nil
639 dabbrev--last-buffer nil
640 dabbrev--last-buffer-found nil
641 dabbrev--abbrev-char-regexp (or dabbrev-abbrev-char-regexp
642 "\\sw\\|\\s_")
643 dabbrev--check-other-buffers dabbrev-always-check-other-buffers))
644
645 ;;; Find all buffers that are considered "friends" according to the
646 ;;; function pointed out by dabbrev-friend-buffer-function.
647 (defun dabbrev--select-buffers ()
648 (save-excursion
649 (and (window-minibuffer-p (selected-window))
650 (set-buffer (dabbrev--minibuffer-origin)))
651 (let ((orig-buffer (current-buffer)))
652 (loop for buffer
653 in (buffer-list)
654 if (and (not (eq orig-buffer buffer))
655 (boundp 'dabbrev-friend-buffer-function)
656 (funcall dabbrev-friend-buffer-function buffer))
657 collect buffer))))
658
659 ;;; Try to find ABBREV in REVERSE direction N times.
660 (defun dabbrev--try-find (abbrev reverse n ignore-case)
661 (save-excursion
662 (let ((case-fold-search-is-local (memq 'case-fold-search
663 (buffer-local-variables)))
664 (expansion nil))
665 (and dabbrev--last-expansion-location
666 (goto-char dabbrev--last-expansion-location))
667 (unwind-protect
668 (progn
669 (or case-fold-search-is-local
670 (make-local-variable 'case-fold-search))
671 ;; Tricky! If `case-fold-search' isn't buffer-local, then
672 ;; this innocent let creates a buffer-local variable and
673 ;; when the let returns, it is still there! The
674 ;; unwind-protect stuff around this makes sure that there
675 ;; exists one before the let, and removes it afterwards.
676 (let ((case-fold-search ignore-case))
677 (loop repeat n
678 while (setq expansion (dabbrev--search abbrev
679 reverse
680 ignore-case)))))
681 (or case-fold-search-is-local
682 (kill-local-variable 'case-fold-search)))
683 (and expansion
684 (setq dabbrev--last-expansion-location (point)))
685 expansion)))
686
687 ;;; Find all expansions of ABBREV
688 (defun dabbrev--find-all-expansions (abbrev ignore-case)
689 (let ((all-expansions nil)
690 expansion)
691 (save-excursion
692 (goto-char (point-min))
693 (while (setq expansion (dabbrev--find-expansion abbrev -1 ignore-case))
694 (push expansion all-expansions)))
695 all-expansions))
696
697 (defun dabbrev--scanning-message ()
698 (message "Scanning '%s'" (buffer-name (current-buffer))))
699
700 ;;; Find one occasion of ABBREV.
701 ;;; DIRECTION > 0 means look that many times backwards.
702 ;;; DIRECTION < 0 means look that many times forward.
703 ;;; DIRECTION = 0 means try both backward and forward.
704 ;;; IGNORE-CASE non-nil means ignore case when searching.
705 (defun dabbrev--find-expansion (abbrev direction ignore-case)
706 (let (expansion)
707 (save-excursion
708 (cond
709 (dabbrev--last-buffer
710 (set-buffer dabbrev--last-buffer)
711 (dabbrev--scanning-message))
712 ((and (not dabbrev-search-these-buffers-only)
713 (window-minibuffer-p (selected-window)))
714 (set-buffer (dabbrev--minibuffer-origin))
715 ;; In the minibuffer-origin buffer we will only search from
716 ;; the top and down.
717 (goto-char (point-min))
718 (setq direction -1)
719 (dabbrev--scanning-message)))
720 (cond
721 ;; ------------------------------------------
722 ;; Look backwards
723 ;; ------------------------------------------
724 ((and (not dabbrev-search-these-buffers-only)
725 (>= direction 0)
726 (setq dabbrev--last-direction (min 1 direction))
727 (setq expansion (dabbrev--try-find abbrev t
728 (max 1 direction)
729 ignore-case)))
730 expansion)
731 ;; ------------------------------------------
732 ;; Look forward
733 ;; ------------------------------------------
734 ((and (or (not dabbrev-search-these-buffers-only)
735 dabbrev--last-buffer)
736 (<= direction 0)
737 (setq dabbrev--last-direction -1)
738 (setq expansion (dabbrev--try-find abbrev nil
739 (max 1 (- direction))
740 ignore-case)))
741 expansion)
742 ;; ------------------------------------------
743 ;; Look in other buffers.
744 ;; Start at (point-min) and look forward.
745 ;; ------------------------------------------
746 (t
747 (setq dabbrev--last-direction -1)
748 ;; Make sure that we should check other buffers
749 (or dabbrev--friend-buffer-list
750 dabbrev--last-buffer
751 (setq dabbrev--friend-buffer-list
752 (mapcar (function get-buffer)
753 dabbrev-search-these-buffers-only))
754 (not dabbrev--check-other-buffers)
755 (not (or (eq dabbrev--check-other-buffers t)
756 (progn
757 (setq dabbrev--check-other-buffers
758 (y-or-n-p "Check in other buffers this time? ")))))
759 (let* (friend-buffer-list non-friend-buffer-list)
760 (setq dabbrev--friend-buffer-list
761 (funcall dabbrev-select-buffers-function))
762 (when dabbrev-check-rest-of-buffers
763 (setq non-friend-buffer-list
764 (nreverse
765 (loop for buffer
766 in (buffer-list)
767 if (not (memq buffer dabbrev--friend-buffer-list))
768 collect buffer)))
769 (setq dabbrev--friend-buffer-list
770 (append dabbrev--friend-buffer-list
771 non-friend-buffer-list)))))
772 ;; Walk through the buffers
773 (while (and (not expansion) dabbrev--friend-buffer-list)
774 (setq dabbrev--last-buffer
775 (car dabbrev--friend-buffer-list))
776 (setq dabbrev--friend-buffer-list
777 (cdr dabbrev--friend-buffer-list))
778 (set-buffer dabbrev--last-buffer)
779 (dabbrev--scanning-message)
780 (setq dabbrev--last-expansion-location (point-min))
781 (setq expansion (dabbrev--try-find abbrev nil 1 ignore-case)))
782 expansion)))))
783
784 (eval-when-compile (require 'picture))
785
786 (defun dabbrev--safe-replace-match (string &optional fixedcase literal)
787 (if (eq major-mode 'picture-mode)
788 (picture-replace-match string fixedcase literal)
789 (replace-match string fixedcase literal)))
790
791 ;;;----------------------------------------------------------------
792 ;;; Substitute the current string in buffer with the expansion
793 ;;; OLD is nil or the last expansion substring.
794 ;;; ABBREV is the abbreviation we are working with.
795 ;;; EXPANSION is the expansion substring.
796 (defun dabbrev--substitute-expansion (old abbrev expansion)
797 ;;(undo-boundary)
798 (let ((use-case-replace (and (eval dabbrev-case-fold-search)
799 (or (not dabbrev-upcase-means-case-search)
800 (string= abbrev (downcase abbrev)))
801 (eval dabbrev-case-replace))))
802 (and nil use-case-replace
803 (setq old (concat abbrev (or old "")))
804 (setq expansion (concat abbrev expansion)))
805 (if old
191 (save-excursion 806 (save-excursion
192 (replace-match abbrev t 'literal)) 807 (search-backward old))
193 ;;; This used to be necessary, but no longer, 808 ;;(store-match-data (list (point-marker) (point-marker)))
194 ;;; because now point is preserved correctly above. 809 (search-backward abbrev))
195 ;;; (search-forward abbrev) 810 ;; Make case of replacement conform to case of abbreviation
196 (if (eq major-mode 'picture-mode) 811 ;; provided (1) that kind of thing is enabled in this buffer
197 (picture-replace-match (if do-case (downcase expansion) expansion) 812 ;; and (2) the replacement itself is all lower case.
198 (not do-case) 813 (dabbrev--safe-replace-match expansion
199 'literal) 814 (not use-case-replace)
200 (replace-match (if do-case (downcase expansion) expansion) 815 t)))
201 (not do-case) 816
202 'literal))) 817
203 ;; Save state for re-expand. 818 ;;;----------------------------------------------------------------
204 (setq last-dabbrevs-abbreviation abbrev) 819 ;;; Search function used by dabbrevs library.
205 (setq last-dabbrevs-expansion expansion) 820
206 (setq last-dabbrevs-expansion-location loc)))) 821 ;;; ABBREV is string to find as prefix of word. Second arg, REVERSE,
207 822 ;;; is t for reverse search, nil for forward. Variable dabbrev-limit
208 ;;;###autoload (define-key esc-map "/" 'dabbrev-expand) 823 ;;; controls the maximum search region size. Third argment IGNORE-CASE
209 824 ;;; non-nil means treat case as insignificant while looking for a match
210 825 ;;; and when comparing with previous matches. Also if that's non-nil
211 ;; Search function used by dabbrevs library. 826 ;;; and the match is found at the beginning of a sentence and is in
212 ;; First arg is string to find as prefix of word. Second arg is 827 ;;; lower case except for the initial then it is converted to all lower
213 ;; t for reverse search, nil for forward. Variable dabbrevs-limit 828 ;;; case for return.
214 ;; controls the maximum search region size. 829
215 830 ;;; Table of expansions already seen is examined in buffer
216 ;; Table of expansions already seen is examined in buffer last-dabbrev-table, 831 ;;; `dabbrev--last-table' so that only distinct possibilities are found
217 ;; so that only distinct possibilities are found by dabbrevs-re-expand. 832 ;;; by dabbrev-re-expand.
218 ;; Note that to prevent finding the abbrev itself it must have been 833
219 ;; entered in the table. 834 ;;; Value is the expansion, or nil if not found.
220 835
221 ;; IGNORE-CASE non-nil means treat case as insignificant while 836 (defun dabbrev--search (abbrev reverse ignore-case)
222 ;; looking for a match and when comparing with previous matches. 837 (save-match-data
223 ;; Also if that's non-nil and the match is found at the beginning of a sentence 838 (let ((pattern1 (concat (regexp-quote abbrev)
224 ;; and is in lower case except for the initial 839 "\\(" dabbrev--abbrev-char-regexp "\\)"))
225 ;; then it is converted to all lower case for return. 840 (pattern2 (concat (regexp-quote abbrev)
226 841 "\\(\\(" dabbrev--abbrev-char-regexp "\\)+\\)"))
227 ;; Value is the expansion, or nil if not found. After a successful 842 (found-string nil))
228 ;; search, point is left right after the expansion found. 843 ;; Limited search.
229 844 (save-restriction
230 (defun dabbrevs-search (pattern reverse ignore-case) 845 (and dabbrev-limit
231 (let (missing result (case-fold-search ignore-case)) 846 (narrow-to-region dabbrev--last-expansion-location
232 (save-restriction ; Uses restriction for limited searches. 847 (+ (point)
233 (if dabbrevs-limit 848 (if reverse (- dabbrev-limit) dabbrev-limit))))
234 (narrow-to-region last-dabbrevs-abbrev-location 849 ;;--------------------------------
235 (+ (point) 850 ;; Look for a distinct expansion, using dabbrev--last-table.
236 (* dabbrevs-limit (if reverse -1 1))))) 851 ;;--------------------------------
237 ;; Keep looking for a distinct expansion. 852 (while (and (not found-string)
238 (setq result nil) 853 (if reverse
239 (setq missing nil) 854 (re-search-backward pattern1 nil t)
240 (while (and (not result) (not missing)) 855 (re-search-forward pattern1 nil t)))
241 ; Look for it, leave loop if search fails. 856 (cond
242 (setq missing 857 ((progn
243 (not (if reverse 858 (goto-char (match-beginning 0))
244 (re-search-backward pattern nil t) 859 (dabbrev--goto-start-of-abbrev)
245 (re-search-forward pattern nil t)))) 860 (/= (point) (match-beginning 0)))
246 861 ;; Prefix of found abbreviation not OK
247 (if (not missing) 862 nil)
248 (progn 863 (t
249 (setq result (buffer-substring (match-beginning 0) 864 (goto-char (match-beginning 0))
250 (match-end 0))) 865 (re-search-forward pattern2)
251 (let* ((test last-dabbrev-table)) 866 (setq found-string
252 (while (and test 867 (buffer-substring (match-beginning 1) (match-end 1)))
253 (not 868 (and ignore-case (setq found-string (downcase found-string)))
254 (if ignore-case 869 ;; Throw away if found in table
255 (string= (downcase (car test)) 870 (when (some
256 (downcase result)) 871 (function
257 (string= (car test) result)))) 872 (lambda (table-string) (string= found-string table-string)))
258 (setq test (cdr test))) 873 dabbrev--last-table)
259 (if test (setq result nil)))))) ; if already in table, ignore 874 (setq found-string nil))))
260 (if result 875 (if reverse
261 (save-excursion 876 (goto-char (match-beginning 0))
262 (let ((beg (match-beginning 0))) 877 (goto-char (match-end 0))))
263 (goto-char beg) 878 (cond
264 (and ignore-case 879 (found-string
265 (string= (substring result 1) 880 ;;--------------------------------
266 (downcase (substring result 1))) 881 ;; Put in `dabbrev--last-table' and decide if we should return
267 (if (string= paragraph-start 882 ;; result or (downcase result)
268 (concat "^$\\|" page-delimiter)) 883 ;;--------------------------------
269 (and (re-search-backward sentence-end nil t) 884 (push found-string dabbrev--last-table)
270 (= (match-end 0) beg)) 885 (let ((result (buffer-substring (match-beginning 0) (match-end 0))))
271 (forward-char 1) 886 (if (and ignore-case (eval dabbrev-case-replace))
272 (backward-sentence) 887 (downcase result)
273 (= (point) beg)) 888 result))))))))
274 (setq result (downcase result)))))) 889
275 result))) 890 (provide 'new-dabbrev)
276
277 (provide 'dabbrev) 891 (provide 'dabbrev)
278 892 ;; new-dabbrev.el ends here
279 ;;; dabbrev.el ends here 893
894