+;; along with GNU Emacs; see the file COPYING. If not, write to the
+;; Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+;; Boston, MA 02110-1301, USA.
+
+;;; Commentary:
+
+;; The purpose with this package is to let you write just a few
+;; characters of words you've written earlier to be able to expand
+;; them.
+;;
+;; To expand a word, just put the point right after the word and press
+;; M-/ (dabbrev-expand) or M-C-/ (dabbrev-completion).
+;;
+;; Check out the customizable variables below to learn about all the
+;; features of this package.
+
+;;; Hints and tips for major modes writers:
+
+;; Recommended values C/Lisp etc text
+;; dabbrev-case-fold-search nil t
+;; dabbrev-case-replace nil t
+;;
+;; Set the variables you want special for your mode like this:
+;; (set (make-local-variable 'dabbrev-case-replace) nil)
+;; Then you don't interfere with other modes.
+;;
+;; If your mode handles buffers that refers to other buffers
+;; (i.e. compilation-mode, gud-mode), then try to set
+;; `dabbrev-select-buffers-function' or `dabbrev-friend-buffer-function'
+;; to a function that point out those buffers.
+
+;; Same goes for major-modes that are connected to other modes. There
+;; are for instance a number of mail-modes. One for reading, one for
+;; creating a new mail etc. Maybe those should be connected.
+
+;; Example for GNUS (when we write a reply, we want dabbrev to look in
+;; the article for expansion):
+;; (set (make-local-variable 'dabbrev-friend-buffer-function)
+;; (lambda (buffer)
+;; (save-excursion
+;; (set-buffer buffer)
+;; (memq major-mode '(news-reply-mode gnus-article-mode)))))
+
+
+;; Known bugs and limitations.
+;; - Possible to do several levels of `dabbrev-completion' in the
+;; minibuffer.
+;; - dabbrev-completion doesn't handle resetting the globals variables
+;; right. It resets them after finding the abbrev.
+
+;; Future enhancements
+;; - Check the tags-files? Like tags-complete?
+;; - Add the possibility of searching both forward and backward to
+;; the nearest expansion.
+;; - Check the kill-ring when everything else fails. (Maybe something
+;; for hippie-expand?). [Bng] <boris@cs.rochester.edu>
+
+;;; These people gave suggestions:
+;; [hymie] Hyman Rosen <marks!hymie@jyacc.jyacc.com>
+;; [burgett] Steve Burgett <burgett@bizet.eecs.berkeley.edu>
+;; [jules] Julian Gosnell <jules@x.co.uk>
+;; [kifer] Michael Kifer <kifer@sbcs.sunysb.edu>
+;; [ake] Ake Stenhoff <extaksf@aom.ericsson.se>
+;; [alon] Alon Albert <al%imercury@uunet.uu.net>
+;; [tromey] Tom Tromey <tromey@busco.lanl.gov>
+;; [Rolf] Rolf Schreiber <rolf@mathematik.uni-stuttgart.de>
+;; [Petri] Petri Raitio <per@tekla.fi>
+;; [ejb] Jay Berkenbilt <ejb@ql.org>
+;; [hawley] Bob Hawley <rth1@quartet.mt.att.com>
+;; ... and to all the people who have participated in the beta tests.
+
+;;; Code:
+
+;;----------------------------------------------------------------
+;; Customization variables
+;;----------------------------------------------------------------
+
+(defgroup dabbrev nil
+ "Dynamic Abbreviations."
+ :tag "Dynamic Abbreviations"
+ :group 'abbrev
+ :group 'convenience)
+
+(defcustom dabbrev-backward-only nil
+ "*If non-nil, `dabbrev-expand' only looks backwards."
+ :type 'boolean
+ :group 'dabbrev)
+
+(defcustom dabbrev-limit nil
+ "*Limits region searched by `dabbrev-expand' to this many chars away."
+ :type '(choice (const :tag "off" nil)
+ integer)
+ :group 'dabbrev)
+
+(defcustom dabbrev-abbrev-skip-leading-regexp nil
+ "*Regexp for skipping leading characters of an abbreviation.
+
+Example: Set this to \"\\\\$\" for programming languages
+in which variable names may appear with or without a leading `$'.
+\(For example, in Makefiles.\)
+
+Set this to nil if no characters should be skipped."
+ :type '(choice regexp
+ (const :tag "off" nil))
+ :group 'dabbrev)
+
+(defcustom dabbrev--eliminate-newlines t
+ "*Non-nil means dabbrev should not insert newlines.
+Instead it converts them to spaces."
+ :type 'boolean
+ :group 'dabbrev)
+
+(defcustom dabbrev-case-fold-search 'case-fold-search
+ "*Control whether dabbrev searches should ignore case.
+A value of nil means case is significant.
+A value of `case-fold-search' means case is significant
+ if `case-fold-search' is nil.
+Any other non-nil version means case is not significant."
+ :type '(choice (const :tag "off" nil)
+ (const :tag "like search" case-fold-search)
+ (other :tag "on" t))
+ :group 'dabbrev)
+
+(defcustom dabbrev-upcase-means-case-search nil
+ "*The significance of an uppercase character in an abbreviation.
+nil means case fold search when searching for possible expansions;
+non-nil means case sensitive search.
+
+This variable has an effect only when the value of
+`dabbrev-case-fold-search' says to ignore case."
+ :type 'boolean
+ :group 'dabbrev)
+
+(defcustom dabbrev-case-distinction 'case-replace
+ "*Whether dabbrev treats expansions as the same if they differ in case.
+
+A value of nil means treat them as different.
+A value of `case-replace' means distinguish them if `case-replace' is nil.
+Any other non-nil value means to treat them as the same.
+
+This variable has an effect only when the value of
+`dabbrev-case-fold-search' specifies to ignore case."
+ :type '(choice (const :tag "off" nil)
+ (const :tag "based on `case-replace'" case-replace)
+ (other :tag "on" t))
+ :group 'dabbrev
+ :version "22.1")
+
+(defcustom dabbrev-case-replace 'case-replace
+ "*Whether dabbrev applies the abbreviations's case pattern to the expansion.
+
+A value of nil means preserve the expansion's case pattern.
+A value of `case-replace' means preserve it if `case-replace' is nil.
+Any other non-nil value means modify the expansion
+by applying the abbreviation's case pattern to it.
+
+This variable has an effect only when the value of
+`dabbrev-case-fold-search' specifies to ignore case."
+ :type '(choice (const :tag "off" nil)
+ (const :tag "based on `case-replace'" case-replace)
+ (other :tag "on" t))
+ :group 'dabbrev)
+
+(defcustom dabbrev-abbrev-char-regexp nil
+ "*Regexp to recognize a character in an abbreviation or expansion.
+This regexp will be surrounded with \\\\( ... \\\\) when actually used.
+
+Set this variable to \"\\\\sw\" if you want ordinary words or
+\"\\\\sw\\\\|\\\\s_\" if you want symbols (including characters whose
+syntax is \"symbol\" as well as those whose syntax is \"word\".
+
+The value nil has a special meaning: the abbreviation is from point to
+previous word-start, but the search is for symbols.
+
+For instance, if you are programming in Lisp, `yes-or-no-p' is a symbol,
+while `yes', `or', `no' and `p' are considered words. If this
+variable is nil, then expanding `yes-or-no-' looks for a symbol
+starting with or containing `no-'. If you set this variable to
+\"\\\\sw\\\\|\\\\s_\", that expansion looks for a symbol starting with
+`yes-or-no-'. Finally, if you set this variable to \"\\\\sw\", then
+expanding `yes-or-no-' signals an error because `-' is not part of a word;
+but expanding `yes-or-no' looks for a word starting with `no'.
+
+The recommended value is \"\\\\sw\\\\|\\\\s_\"."
+ :type '(choice (const nil)
+ regexp)
+ :group 'dabbrev)
+
+(defcustom dabbrev-check-all-buffers t
+ "*Non-nil means dabbrev package should search *all* buffers.
+
+Dabbrev always searches the current buffer first. Then, if
+`dabbrev-check-other-buffers' says so, it searches the buffers
+designated by `dabbrev-select-buffers-function'.
+
+Then, if `dabbrev-check-all-buffers' is non-nil, dabbrev searches
+all the other buffers, except those named in `dabbrev-ignored-buffer-names',
+or matched by `dabbrev-ignored-regexps'."
+ :type 'boolean
+ :group 'dabbrev)
+
+(defcustom dabbrev-ignored-buffer-names '("*Messages*" "*Buffer List*")
+ "*List of buffer names that dabbrev should not check.
+See also `dabbrev-ignored-buffer-regexps'."
+ :type '(repeat (string :tag "Buffer name"))
+ :group 'dabbrev
+ :version "20.3")
+
+(defcustom dabbrev-ignored-buffer-regexps nil
+ "*List of regexps matching names of buffers that dabbrev should not check.
+See also `dabbrev-ignored-buffer-names'."
+ :type '(repeat regexp)
+ :group 'dabbrev
+ :version "21.1")
+
+(defcustom dabbrev-check-other-buffers t
+ "*Should \\[dabbrev-expand] look in other buffers?\
+
+nil: Don't look in other buffers.
+t: Also look for expansions in the buffers pointed out by
+ `dabbrev-select-buffers-function'.
+Anything else: When we can't find any more expansions in
+the current buffer, then ask the user whether to look in other
+buffers too.
+
+The default value is t."
+ :type '(choice (const :tag "off" nil)
+ (const :tag "on" t)
+ (other :tag "ask" other))
+ :group 'dabbrev)
+
+;; I guess setting this to a function that selects all C- or C++-
+;; mode buffers would be a good choice for a debugging buffer,
+;; when debugging C- or C++-code.
+(defvar dabbrev-select-buffers-function 'dabbrev--select-buffers
+ "A function that selects buffers that should be searched by dabbrev.
+The function should take no arguments and return a list of buffers to
+search for expansions. See the source of `dabbrev--select-buffers'
+for an example.
+
+A mode setting this variable should make it buffer local.")
+
+(defcustom dabbrev-friend-buffer-function 'dabbrev--same-major-mode-p
+ "*A function to decide whether dabbrev should search OTHER-BUFFER.
+The function should take one argument, OTHER-BUFFER, and return
+non-nil if that buffer should be searched. Have a look at
+`dabbrev--same-major-mode-p' for an example.
+
+The value of `dabbrev-friend-buffer-function' has an effect only if
+the value of `dabbrev-select-buffers-function' uses it. The function
+`dabbrev--select-buffers' is one function you can use here.
+
+A mode setting this variable should make it buffer local."
+ :type 'function
+ :group 'dabbrev)
+
+(defcustom dabbrev-search-these-buffers-only nil
+ "If non-nil, a list of buffers which dabbrev should search.
+If this variable is non-nil, dabbrev will only look in these buffers.
+It will not even look in the current buffer if it is not a member of
+this list."
+ :group 'dabbrev)
+
+;;----------------------------------------------------------------
+;; Internal variables
+;;----------------------------------------------------------------
+
+;; Last obarray of completions in `dabbrev-completion'
+(defvar dabbrev--last-obarray nil)
+
+;; Table of expansions seen so far
+(defvar dabbrev--last-table nil)
+
+;; Last string we tried to expand.
+(defvar dabbrev--last-abbreviation nil)
+
+;; Location last abbreviation began
+(defvar dabbrev--last-abbrev-location nil)
+
+;; Direction of last dabbrevs search
+(defvar dabbrev--last-direction 0)
+
+;; Last expansion of an abbreviation.
+(defvar dabbrev--last-expansion nil)
+
+;; Location the last expansion was found.
+(defvar dabbrev--last-expansion-location nil)
+
+;; The list of remaining buffers with the same mode as current buffer.
+(defvar dabbrev--friend-buffer-list nil)
+
+;; The buffer we looked in last, not counting the current buffer.
+(defvar dabbrev--last-buffer nil)
+
+;; The buffer we found the expansion last time.
+(defvar dabbrev--last-buffer-found nil)
+
+;; The buffer we last did a completion in.
+(defvar dabbrev--last-completion-buffer nil)
+
+;; If non-nil, a function to use when copying successive words.
+;; It should be `upcase' or `downcase'.
+(defvar dabbrev--last-case-pattern nil)
+
+;; Same as dabbrev-check-other-buffers, but is set for every expand.
+(defvar dabbrev--check-other-buffers dabbrev-check-other-buffers)
+
+;; The regexp for recognizing a character in an abbreviation.
+(defvar dabbrev--abbrev-char-regexp nil)
+
+;;----------------------------------------------------------------
+;; Macros
+;;----------------------------------------------------------------
+
+;;; Get the buffer that mini-buffer was activated from
+(defsubst dabbrev--minibuffer-origin ()
+ (car (cdr (buffer-list))))
+
+;; Make a list of some of the elements of LIST.
+;; Check each element of LIST, storing it temporarily in the
+;; variable ELEMENT, and include it in the result
+;; if CONDITION evaluates non-nil.
+(defmacro dabbrev-filter-elements (element list condition)
+ `(let (dabbrev-result dabbrev-tail ,element)
+ (setq dabbrev-tail ,list)
+ (while dabbrev-tail
+ (setq ,element (car dabbrev-tail))
+ (if ,condition
+ (setq dabbrev-result (cons ,element dabbrev-result)))
+ (setq dabbrev-tail (cdr dabbrev-tail)))
+ (nreverse dabbrev-result)))
+
+;;----------------------------------------------------------------
+;; Exported functions
+;;----------------------------------------------------------------
+
+;;;###autoload (define-key esc-map "/" 'dabbrev-expand)
+;;;??? Do we want this?
+;;;###autoload (define-key esc-map [?\C-/] 'dabbrev-completion)
+
+;;;###autoload
+(defun dabbrev-completion (&optional arg)
+ "Completion on current word.
+Like \\[dabbrev-expand] but finds all expansions in the current buffer
+and presents suggestions for completion.
+
+With a prefix argument, it searches all buffers accepted by the
+function pointed out by `dabbrev-friend-buffer-function' to find the
+completions.
+
+If the prefix argument is 16 (which comes from C-u C-u),
+then it searches *all* buffers.
+
+With no prefix argument, it reuses an old completion list
+if there is a suitable one already."
+
+ (interactive "*P")
+ (dabbrev--reset-global-variables)
+ (let* ((dabbrev-check-other-buffers (and arg t))
+ (dabbrev-check-all-buffers
+ (and arg (= (prefix-numeric-value arg) 16)))
+ (abbrev (dabbrev--abbrev-at-point))
+ (ignore-case-p (and (if (eq dabbrev-case-fold-search 'case-fold-search)
+ case-fold-search
+ dabbrev-case-fold-search)
+ (or (not dabbrev-upcase-means-case-search)
+ (string= abbrev (downcase abbrev)))))
+ (my-obarray dabbrev--last-obarray)
+ init)
+ (save-excursion
+ (if (and (null arg)
+ my-obarray
+ (or (eq dabbrev--last-completion-buffer (current-buffer))
+ (and (window-minibuffer-p (selected-window))
+ (eq dabbrev--last-completion-buffer
+ (dabbrev--minibuffer-origin))))
+ dabbrev--last-abbreviation
+ (>= (length abbrev) (length dabbrev--last-abbreviation))
+ (string= dabbrev--last-abbreviation
+ (substring abbrev 0
+ (length dabbrev--last-abbreviation)))
+ (setq init (try-completion abbrev my-obarray)))
+ ;; We can reuse the existing completion list.
+ nil
+ ;;--------------------------------
+ ;; New abbreviation to expand.
+ ;;--------------------------------
+ (setq dabbrev--last-abbreviation abbrev)
+ ;; Find all expansion
+ (let ((completion-list
+ (dabbrev--find-all-expansions abbrev ignore-case-p))
+ (completion-ignore-case ignore-case-p))
+ ;; Make an obarray with all expansions
+ (setq my-obarray (make-vector (length completion-list) 0))
+ (or (> (length my-obarray) 0)
+ (error "No dynamic expansion for \"%s\" found%s"
+ abbrev
+ (if dabbrev--check-other-buffers "" " in this-buffer")))
+ (cond
+ ((or (not ignore-case-p)
+ (not dabbrev-case-replace))
+ (mapc (function (lambda (string)
+ (intern string my-obarray)))
+ completion-list))
+ ((string= abbrev (upcase abbrev))
+ (mapc (function (lambda (string)
+ (intern (upcase string) my-obarray)))
+ completion-list))
+ ((string= (substring abbrev 0 1)
+ (upcase (substring abbrev 0 1)))
+ (mapc (function (lambda (string)
+ (intern (capitalize string) my-obarray)))
+ completion-list))
+ (t
+ (mapc (function (lambda (string)
+ (intern (downcase string) my-obarray)))
+ completion-list)))
+ (setq dabbrev--last-obarray my-obarray)
+ (setq dabbrev--last-completion-buffer (current-buffer))
+ ;; Find the longest common string.
+ (setq init (try-completion abbrev my-obarray)))))
+ ;;--------------------------------
+ ;; Let the user choose between the expansions
+ ;;--------------------------------
+ (or (stringp init)
+ (setq init abbrev))
+ (cond
+ ;; * Replace string fragment with matched common substring completion.
+ ((and (not (string-equal init ""))
+ (not (string-equal (downcase init) (downcase abbrev))))
+ (if (> (length (all-completions init my-obarray)) 1)
+ (message "Repeat `%s' to see all completions"
+ (key-description (this-command-keys)))
+ (message "The only possible completion"))
+ (dabbrev--substitute-expansion nil abbrev init nil))
+ (t
+ ;; * String is a common substring completion already. Make list.
+ (message "Making completion list...")
+ (with-output-to-temp-buffer "*Completions*"
+ (display-completion-list (all-completions init my-obarray)))
+ (message "Making completion list...done")))
+ (and (window-minibuffer-p (selected-window))
+ (message nil))))