123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267 |
- ;;; message-x.el --- customizable completion in message headers
- ;; Copyright (C) 1998 Kai Großjohann
- ;; $Id: message-x.el,v 1.23 2001/05/30 21:04:47 grossjoh Exp $
- ;; Author: Kai Grossjohann <grossjohann@ls6.informatik.uni-dortmund.de>
- ;; Keywords: news, mail, compose, completion
- ;; This file is not part of GNU Emacs.
- ;; This is free software; you can redistribute it and/or modify
- ;; it under the terms of the GNU General Public License as published by
- ;; the Free Software Foundation; either version 2, or (at your option)
- ;; any later version.
- ;; GNU Emacs is distributed in the hope that it will be useful,
- ;; but WITHOUT ANY WARRANTY; without even the implied warranty of
- ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- ;; GNU General Public License for more details.
- ;; You should have received a copy of the GNU General Public License
- ;; along with GNU Emacs; see the file COPYING. If not, write to the
- ;; Free Software Foundation, Inc., 59 Temple Place - Suite 330,
- ;; Boston, MA 02111-1307, USA.
- ;;; Commentary:
- ;; The most recent version of this can always be fetched from the
- ;; following FTP site:
- ;; ls6-ftp.cs.uni-dortmund.de:/pub/src/emacs
- ;; Installation:
- ;;
- ;; You must be using Gnus 5 or higher for this to work. Installation
- ;; is simple: just put this file somewhere in your load-path, run M-x
- ;; byte-compile-file RET, and put the following line in your .gnus file:
- ;;
- ;; (require 'message-x)
- ;;
- ;; Customization is possible through the two variables
- ;; message-x-body-function and message-x-completion-alist, which see.
- ;;
- ;; Purpose:
- ;;
- ;; This assigns a context-sensitive function to the TAB key in message
- ;; mode of Gnus. When in a header line, this performs completion
- ;; based on which header we're in (for example, newsgroup name
- ;; completion makes sense in the Newsgroups header whereas mail alias
- ;; expansion makes sense in the To and Cc headers). When in the
- ;; message body, this executes a different function, by default it is
- ;; indent-relative.
- ;;
- ;; To be more precise, the mechanism is as follows. When point is in
- ;; a known header (a header mentioned in
- ;; `message-x-completion-alist'), then the completion function thus
- ;; specified is executed. For the To and Cc headers, this could be
- ;; `bbdb-complete-name', for example. Then we look if the completion
- ;; function has done anything. If the completion function has NOT
- ;; done anything, then we invoke the function specified by
- ;; `message-x-unknown-header-function'.
- ;;
- ;; When point is in an unknown header (not mentioned in
- ;; `message-x-completion-alist'), then we invoke the function
- ;; specified by `message-x-unknown-header-function'. This function
- ;; could advance point to the next header, for example. (In fact,
- ;; that's the default behavior.)
- ;;
- ;; When point is not in a header (but in the body), then we invoke the
- ;; function specified by `message-x-body-function'. By default, this
- ;; is `indent-relative' -- the default indentation function for text
- ;; mode.
- ;;; Setup Code:
- (defconst message-x-version "$Id: message-x.el,v 1.23 2001/05/30 21:04:47 grossjoh Exp $"
- "Version of message-x.")
- (require 'message)
- ;;; User Customizable Variables:
- (defgroup message-x nil
- "Customizable completion in message headers.")
- (defcustom message-x-body-function 'indent-relative
- "message-x-tab executes this if point is in the body of a message."
- :type '(function)
- :group 'message-x)
- (defcustom message-x-unknown-header-function 'message-position-point
- "message-x-tab executes this if point is in an unknown header.
- This function is also executed on known headers if the completion
- function didn't find anything to do."
- :type '(function)
- :group 'message-x)
- (defcustom message-x-completion-alist
- '(("\\([rR]esent-\\|[rR]eply-\\)?[tT]o:\\|[bB]?[cC][cC]:" .
- message-x-complete-name)
- ((if (boundp 'message-newgroups-header-regexp)
- message-newgroups-header-regexp
- message-newsgroups-header-regexp) . message-expand-group))
- "Table telling which completion function `message-x-tab' should invoke.
- Table is a list of pairs (GROUP . FUNC). GROUP is evaled to produce a
- regexp specifying the header names. See `mail-abbrev-in-expansion-header-p'
- for details on the regexp. If point is in that header, FUNC is invoked
- via `message-x-call-completion-function'."
- :type '(repeat (cons string function))
- :group 'message-x)
- (defcustom message-x-before-completion-functions nil
- "`message-x-tab' runs these functions before doing the actual
- completion. The functions are called with one argument, a string
- specifying the current header name in lower case or nil, which
- specifies the message body. Also see `message-x-after-completion-hook'."
- :type 'hook
- :group 'message-x)
- (defcustom message-x-after-completion-functions nil
- "`message-x-tab' runs these functions after doing the actual
- completion. The functions are called with one argument, a string
- specifying the current header name in lower case or nil, which
- specifies the message body. Also see `message-x-before-completion-hook'."
- :type 'hook
- :group 'message-x)
- ;;; Internal variables:
- (defvar message-x-displayed-completion-buffer-flag nil
- "Set to `t' from `completion-setup-hook'.
- `message-x-call-completion-function' uses this to see if the
- completion function has set up the *Completions* buffer.")
- ;;; Code:
- (defun message-x-in-header-p ()
- "Returns t iff point is in the header section."
- (save-excursion
- (let ((p (point)))
- (goto-char (point-min))
- (and (re-search-forward (concat "^"
- (regexp-quote mail-header-separator)
- "$")
- nil t)
- (progn (beginning-of-line) t)
- (< p (point))))))
- (defun message-x-which-header ()
- "Returns the header we're currently in. Returns nil if not in a header.
- Example: returns \"to\" if we're in the \"to\" header right now."
- (and (message-x-in-header-p)
- (save-excursion
- (beginning-of-line)
- (while (looking-at "^[ \t]+") (forward-line -1))
- (looking-at "\\([^:]+\\):")
- (downcase (buffer-substring-no-properties (match-beginning 1)
- (match-end 1))))))
- (defun message-x-complete-name ()
- "Does name completion in recipient headers."
- (interactive)
- (unless (when abbrev-mode
- (message-x-call-completion-function 'expand-abbrev))
- (cond ((and (boundp 'eudc-server) eudc-server
- (boundp 'eudc-protocol) eudc-protocol)
- (condition-case nil
- (eudc-expand-inline)
- (error nil)))
- ((and (boundp 'bbdb-hashtable) (fboundp 'bbdb-complete-name))
- (let ((bbdb-complete-name-hooks nil))
- (bbdb-complete-name))))))
- (defun message-x-set-displayed-completion-buffer-flag ()
- "Set `message-x-displayed-completion-buffer-flag' to t."
- (setq message-x-displayed-completion-buffer-flag t))
- (add-hook 'completion-setup-hook
- 'message-x-set-displayed-completion-buffer-flag)
- (defun message-x-call-completion-function (func)
- "Calls the given completion function, then checks if completion was done.
- When doing completion, the following cases are possible:
- - The current prefix was complete.
- - Some string was inserted.
- - A list of possible completions was displayed or updated.
- In the first case, the completion function has not done anything, and
- so `message-x-call-completion-function' returns nil. In all other
- cases, `message-x-call-completion-function' returns non-nil."
- (let* ((message-x-displayed-completion-buffer-flag nil)
- (cbuf (get-buffer-create "*Completions*"))
- (cbufcontents (save-excursion
- (set-buffer cbuf)
- (buffer-string)))
- (cwin (get-buffer-window cbuf))
- (thisline (buffer-substring
- (save-excursion
- (beginning-of-line)
- (point))
- (point)))
- (cws (window-start cwin)))
- (funcall func)
- (cond ((not (string= thisline
- (buffer-substring
- (save-excursion
- (beginning-of-line)
- (point))
- (point))))
- t)
- (message-x-displayed-completion-buffer-flag
- (cond ((not (equal cwin (get-buffer-window cbuf)))
- t)
- ((not (string= cbufcontents
- (save-excursion
- (set-buffer cbuf)
- (buffer-string))))
- t)
- ((/= cws (window-start (get-buffer-window cbuf)))
- t)
- (t nil))))))
- ;;;###autoload
- (defun message-x-tab (&optional skip-completion)
- "Smart completion or indentation in message buffers.
- Looks at the position of point to decide what to do. If point is in
- one of the headers specified by `message-x-completion-alist', then the
- completion function specified there is executed. If point is in
- another header (not mentioned there), then the function specified by
- `message-x-unknown-header-function' is executed. If point is in the
- body, the function specified by `message-x-body-function' is executed.
- Completion is magic: after the completion function is executed, checks
- are performed to see if the completion function has actually done
- something. If it has not done anything,
- `message-x-unknown-header-function' is executed. See the function
- `message-x-call-completion-function' for details on how to check
- whether the completion function has done something.
- A non-nil optional arg SKIP-COMPLETION (prefix arg if invoked
- interactively) means to not attempt completion. Instead,
- `message-x-unknown-header-function' function is called in all headers,
- known or unknown."
- (interactive "P")
- (let* ((alist message-x-completion-alist)
- (which-header (message-x-which-header))
- header)
- (run-hook-with-args 'message-x-before-completion-functions which-header)
- (while (and (not skip-completion)
- alist
- (let ((mail-abbrev-mode-regexp (eval (caar alist))))
- (not (mail-abbrev-in-expansion-header-p))))
- (setq alist (cdr alist)))
- (cond ((and alist (not skip-completion))
- (let ((p (point))
- (func (cdar alist)))
- (unless (message-x-call-completion-function func)
- (funcall message-x-unknown-header-function))))
- ((message-x-in-header-p)
- (funcall message-x-unknown-header-function))
- (t
- (funcall message-x-body-function)))
- (run-hook-with-args 'message-x-after-completion-functions which-header)))
- (define-key message-mode-map "\t" 'message-x-tab)
- (provide 'message-x)
- ;;; message-x.el ends here
|