My personal .emacs.d folder

message-x.el 11KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267
  1. ;;; message-x.el --- customizable completion in message headers
  2. ;; Copyright (C) 1998 Kai Großjohann
  3. ;; $Id: message-x.el,v 1.23 2001/05/30 21:04:47 grossjoh Exp $
  4. ;; Author: Kai Grossjohann <grossjohann@ls6.informatik.uni-dortmund.de>
  5. ;; Keywords: news, mail, compose, completion
  6. ;; This file is not part of GNU Emacs.
  7. ;; This is free software; you can redistribute it and/or modify
  8. ;; it under the terms of the GNU General Public License as published by
  9. ;; the Free Software Foundation; either version 2, or (at your option)
  10. ;; any later version.
  11. ;; GNU Emacs is distributed in the hope that it will be useful,
  12. ;; but WITHOUT ANY WARRANTY; without even the implied warranty of
  13. ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  14. ;; GNU General Public License for more details.
  15. ;; You should have received a copy of the GNU General Public License
  16. ;; along with GNU Emacs; see the file COPYING. If not, write to the
  17. ;; Free Software Foundation, Inc., 59 Temple Place - Suite 330,
  18. ;; Boston, MA 02111-1307, USA.
  19. ;;; Commentary:
  20. ;; The most recent version of this can always be fetched from the
  21. ;; following FTP site:
  22. ;; ls6-ftp.cs.uni-dortmund.de:/pub/src/emacs
  23. ;; Installation:
  24. ;;
  25. ;; You must be using Gnus 5 or higher for this to work. Installation
  26. ;; is simple: just put this file somewhere in your load-path, run M-x
  27. ;; byte-compile-file RET, and put the following line in your .gnus file:
  28. ;;
  29. ;; (require 'message-x)
  30. ;;
  31. ;; Customization is possible through the two variables
  32. ;; message-x-body-function and message-x-completion-alist, which see.
  33. ;;
  34. ;; Purpose:
  35. ;;
  36. ;; This assigns a context-sensitive function to the TAB key in message
  37. ;; mode of Gnus. When in a header line, this performs completion
  38. ;; based on which header we're in (for example, newsgroup name
  39. ;; completion makes sense in the Newsgroups header whereas mail alias
  40. ;; expansion makes sense in the To and Cc headers). When in the
  41. ;; message body, this executes a different function, by default it is
  42. ;; indent-relative.
  43. ;;
  44. ;; To be more precise, the mechanism is as follows. When point is in
  45. ;; a known header (a header mentioned in
  46. ;; `message-x-completion-alist'), then the completion function thus
  47. ;; specified is executed. For the To and Cc headers, this could be
  48. ;; `bbdb-complete-name', for example. Then we look if the completion
  49. ;; function has done anything. If the completion function has NOT
  50. ;; done anything, then we invoke the function specified by
  51. ;; `message-x-unknown-header-function'.
  52. ;;
  53. ;; When point is in an unknown header (not mentioned in
  54. ;; `message-x-completion-alist'), then we invoke the function
  55. ;; specified by `message-x-unknown-header-function'. This function
  56. ;; could advance point to the next header, for example. (In fact,
  57. ;; that's the default behavior.)
  58. ;;
  59. ;; When point is not in a header (but in the body), then we invoke the
  60. ;; function specified by `message-x-body-function'. By default, this
  61. ;; is `indent-relative' -- the default indentation function for text
  62. ;; mode.
  63. ;;; Setup Code:
  64. (defconst message-x-version "$Id: message-x.el,v 1.23 2001/05/30 21:04:47 grossjoh Exp $"
  65. "Version of message-x.")
  66. (require 'message)
  67. ;;; User Customizable Variables:
  68. (defgroup message-x nil
  69. "Customizable completion in message headers.")
  70. (defcustom message-x-body-function 'indent-relative
  71. "message-x-tab executes this if point is in the body of a message."
  72. :type '(function)
  73. :group 'message-x)
  74. (defcustom message-x-unknown-header-function 'message-position-point
  75. "message-x-tab executes this if point is in an unknown header.
  76. This function is also executed on known headers if the completion
  77. function didn't find anything to do."
  78. :type '(function)
  79. :group 'message-x)
  80. (defcustom message-x-completion-alist
  81. '(("\\([rR]esent-\\|[rR]eply-\\)?[tT]o:\\|[bB]?[cC][cC]:" .
  82. message-x-complete-name)
  83. ((if (boundp 'message-newgroups-header-regexp)
  84. message-newgroups-header-regexp
  85. message-newsgroups-header-regexp) . message-expand-group))
  86. "Table telling which completion function `message-x-tab' should invoke.
  87. Table is a list of pairs (GROUP . FUNC). GROUP is evaled to produce a
  88. regexp specifying the header names. See `mail-abbrev-in-expansion-header-p'
  89. for details on the regexp. If point is in that header, FUNC is invoked
  90. via `message-x-call-completion-function'."
  91. :type '(repeat (cons string function))
  92. :group 'message-x)
  93. (defcustom message-x-before-completion-functions nil
  94. "`message-x-tab' runs these functions before doing the actual
  95. completion. The functions are called with one argument, a string
  96. specifying the current header name in lower case or nil, which
  97. specifies the message body. Also see `message-x-after-completion-hook'."
  98. :type 'hook
  99. :group 'message-x)
  100. (defcustom message-x-after-completion-functions nil
  101. "`message-x-tab' runs these functions after doing the actual
  102. completion. The functions are called with one argument, a string
  103. specifying the current header name in lower case or nil, which
  104. specifies the message body. Also see `message-x-before-completion-hook'."
  105. :type 'hook
  106. :group 'message-x)
  107. ;;; Internal variables:
  108. (defvar message-x-displayed-completion-buffer-flag nil
  109. "Set to `t' from `completion-setup-hook'.
  110. `message-x-call-completion-function' uses this to see if the
  111. completion function has set up the *Completions* buffer.")
  112. ;;; Code:
  113. (defun message-x-in-header-p ()
  114. "Returns t iff point is in the header section."
  115. (save-excursion
  116. (let ((p (point)))
  117. (goto-char (point-min))
  118. (and (re-search-forward (concat "^"
  119. (regexp-quote mail-header-separator)
  120. "$")
  121. nil t)
  122. (progn (beginning-of-line) t)
  123. (< p (point))))))
  124. (defun message-x-which-header ()
  125. "Returns the header we're currently in. Returns nil if not in a header.
  126. Example: returns \"to\" if we're in the \"to\" header right now."
  127. (and (message-x-in-header-p)
  128. (save-excursion
  129. (beginning-of-line)
  130. (while (looking-at "^[ \t]+") (forward-line -1))
  131. (looking-at "\\([^:]+\\):")
  132. (downcase (buffer-substring-no-properties (match-beginning 1)
  133. (match-end 1))))))
  134. (defun message-x-complete-name ()
  135. "Does name completion in recipient headers."
  136. (interactive)
  137. (unless (when abbrev-mode
  138. (message-x-call-completion-function 'expand-abbrev))
  139. (cond ((and (boundp 'eudc-server) eudc-server
  140. (boundp 'eudc-protocol) eudc-protocol)
  141. (condition-case nil
  142. (eudc-expand-inline)
  143. (error nil)))
  144. ((and (boundp 'bbdb-hashtable) (fboundp 'bbdb-complete-name))
  145. (let ((bbdb-complete-name-hooks nil))
  146. (bbdb-complete-name))))))
  147. (defun message-x-set-displayed-completion-buffer-flag ()
  148. "Set `message-x-displayed-completion-buffer-flag' to t."
  149. (setq message-x-displayed-completion-buffer-flag t))
  150. (add-hook 'completion-setup-hook
  151. 'message-x-set-displayed-completion-buffer-flag)
  152. (defun message-x-call-completion-function (func)
  153. "Calls the given completion function, then checks if completion was done.
  154. When doing completion, the following cases are possible:
  155. - The current prefix was complete.
  156. - Some string was inserted.
  157. - A list of possible completions was displayed or updated.
  158. In the first case, the completion function has not done anything, and
  159. so `message-x-call-completion-function' returns nil. In all other
  160. cases, `message-x-call-completion-function' returns non-nil."
  161. (let* ((message-x-displayed-completion-buffer-flag nil)
  162. (cbuf (get-buffer-create "*Completions*"))
  163. (cbufcontents (save-excursion
  164. (set-buffer cbuf)
  165. (buffer-string)))
  166. (cwin (get-buffer-window cbuf))
  167. (thisline (buffer-substring
  168. (save-excursion
  169. (beginning-of-line)
  170. (point))
  171. (point)))
  172. (cws (window-start cwin)))
  173. (funcall func)
  174. (cond ((not (string= thisline
  175. (buffer-substring
  176. (save-excursion
  177. (beginning-of-line)
  178. (point))
  179. (point))))
  180. t)
  181. (message-x-displayed-completion-buffer-flag
  182. (cond ((not (equal cwin (get-buffer-window cbuf)))
  183. t)
  184. ((not (string= cbufcontents
  185. (save-excursion
  186. (set-buffer cbuf)
  187. (buffer-string))))
  188. t)
  189. ((/= cws (window-start (get-buffer-window cbuf)))
  190. t)
  191. (t nil))))))
  192. ;;;###autoload
  193. (defun message-x-tab (&optional skip-completion)
  194. "Smart completion or indentation in message buffers.
  195. Looks at the position of point to decide what to do. If point is in
  196. one of the headers specified by `message-x-completion-alist', then the
  197. completion function specified there is executed. If point is in
  198. another header (not mentioned there), then the function specified by
  199. `message-x-unknown-header-function' is executed. If point is in the
  200. body, the function specified by `message-x-body-function' is executed.
  201. Completion is magic: after the completion function is executed, checks
  202. are performed to see if the completion function has actually done
  203. something. If it has not done anything,
  204. `message-x-unknown-header-function' is executed. See the function
  205. `message-x-call-completion-function' for details on how to check
  206. whether the completion function has done something.
  207. A non-nil optional arg SKIP-COMPLETION (prefix arg if invoked
  208. interactively) means to not attempt completion. Instead,
  209. `message-x-unknown-header-function' function is called in all headers,
  210. known or unknown."
  211. (interactive "P")
  212. (let* ((alist message-x-completion-alist)
  213. (which-header (message-x-which-header))
  214. header)
  215. (run-hook-with-args 'message-x-before-completion-functions which-header)
  216. (while (and (not skip-completion)
  217. alist
  218. (let ((mail-abbrev-mode-regexp (eval (caar alist))))
  219. (not (mail-abbrev-in-expansion-header-p))))
  220. (setq alist (cdr alist)))
  221. (cond ((and alist (not skip-completion))
  222. (let ((p (point))
  223. (func (cdar alist)))
  224. (unless (message-x-call-completion-function func)
  225. (funcall message-x-unknown-header-function))))
  226. ((message-x-in-header-p)
  227. (funcall message-x-unknown-header-function))
  228. (t
  229. (funcall message-x-body-function)))
  230. (run-hook-with-args 'message-x-after-completion-functions which-header)))
  231. (define-key message-mode-map "\t" 'message-x-tab)
  232. (provide 'message-x)
  233. ;;; message-x.el ends here