home *** CD-ROM | disk | FTP | other *** search
/ InfoMagic Source Code 1993 July / THE_SOURCE_CODE_CD_ROM.iso / languages / elisp / functions / insert-hooks.el < prev    next >
Encoding:
Text File  |  1991-12-03  |  9.6 KB  |  205 lines

  1. ; Path: dg-rtp!uunet!uunet!think.com!yale.edu!yale!mintaka.lcs.mit.edu!ai-lab!life.ai.mit.edu!friedman
  2. ; From: friedman@nutrimat.gnu.ai.mit.edu (Noah Friedman)
  3. ; Newsgroups: gnu.emacs.sources
  4. ; Subject: insert-hooks.el
  5. ; Date: 22 Nov 91 20:13:31 GMT
  6. ; Reply-To: friedman@prep.ai.mit.edu
  7. ; Organization: Free Software Foundation, 675 Mass Ave. Cambridge, MA 02139
  8. ;    Here's something I whipped up the other day primarily so I could add
  9. ; hooks to do-auto-save, only to discover it won't work right because it's a
  10. ; primitive.  Well, maybe someone else will find this useful.
  11. ;;; insert-hooks, a function to insert pre- and post- hooks in an
  12. ;;; arbitrary function.
  13. ;;;
  14. ;;; LCD Archive Entry:
  15. ;;; insert-hooks|Noah Friedman|friedman@nutrimat.gnu.ai.mit.edu
  16. ;;; |Insert pre- and post- hooks in an arbitrary function.
  17. ;;; |91-11-22||~/functions/insert-hooks.el.Z|
  18. ;;;
  19. ;;; Copyright (C) 1991 Noah S. Friedman
  20. ;;;
  21. ;;; This program is free software; you can redistribute it and/or modify
  22. ;;; it under the terms of the GNU General Public License as published by
  23. ;;; the Free Software Foundation; either version 2, or (at your option)
  24. ;;; any later version.
  25. ;;;
  26. ;;; This program is distributed in the hope that it will be useful,
  27. ;;; but WITHOUT ANY WARRANTY; without even the implied warranty of
  28. ;;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  29. ;;; GNU General Public License for more details.
  30. ;;;
  31. ;;; You should have received a copy of the GNU General Public License
  32. ;;; along with this program; if not, you can either send email to this
  33. ;;; program's author (see below) or write to: The Free Software
  34. ;;; Foundation, Inc., 675 Massachusetts Avenue, Cambridge, MA 02139, USA.  
  35. ;;;
  36. ;;; Please send requests for copies of the GPL, bug reports, fixes,
  37. ;;; comments, flames, etc. to friedman@prep.ai.mit.edu
  38. ;;;
  39. ;;; Last modified 22-Nov-91
  40. ;;;
  41.  
  42. ;;;    Have you ever wished that a particular command or lisp function
  43. ;;; ran some sort of hooks so that you could customize its behavior,
  44. ;;; but didn't want to have to maintain your own copy of the function
  45. ;;; in question?  Well, here's a possible alternative. 
  46. ;;;
  47. ;;;    insert-hooks will take a named hook and add forms to the
  48. ;;; indicated function to run that hook as the very first thing it
  49. ;;; does.  If the optional third argument to insert-hooks is non-nil,
  50. ;;; then the forms to run that hook as the very *last* thing will be
  51. ;;; inserted into the function.  This is done in such a way that any
  52. ;;; return value from the original form of the function will not be
  53. ;;; lost. 
  54. ;;;
  55. ;;;    Note: modifying a primitive function can have unexpected
  56. ;;; results, because, while other true lisp routines may call the
  57. ;;; modified function, other subrs will call the original primitive.
  58. ;;; So for example, if you were planning to modify the `do-auto-save'
  59. ;;; subr, emacs would probably not pay any attention to your
  60. ;;; definition because of the way do-auto-save is called (i.e. by
  61. ;;; another subr).  However, it's possible to redefine some subrs and
  62. ;;; get useful results because they are usually only called from lisp
  63. ;;; code (e.g.  `current-time-string').
  64. ;;;
  65. ;;; I don't know for certain how robust this is.  I couldn't think of
  66. ;;; any cases where insert-hook will *break* a function, but I
  67. ;;; definitely want to know if you find an example.  The only bug I
  68. ;;; know of (and it can't be helped) is that the docstring for
  69. ;;; primitive functions will have all the keybindings resolved at the
  70. ;;; time insert-hooks is called, so if you rebind keys later, the
  71. ;;; docstring may be inaccurate.  This is because I had to use the
  72. ;;; `documentation' function which resolves the special syntax for key
  73. ;;; bindings in docstrings.
  74. ;;;
  75. ;;; Do not confuse insert-hooks with the planned-for add-hooks
  76. ;;; primitive in emacs 19.  That function adds forms to a hook itself,
  77. ;;; it doesn't add forms to a function for running hooks.
  78.  
  79. ;;; Thanks to Roland McGrath for several ideas here (via his
  80. ;;; make-interactive function)
  81.  
  82. ;;; Todo: 
  83. ;;;   1) determine whether a given hook is already present in the
  84. ;;;      function, and don't mutilate function more if it is (possibly
  85. ;;;      ask before proceeding)
  86. ;;;   2) Another function, remove-hooks, to undo parts or all of
  87. ;;;      changes made by insert-hooks.
  88. ;;;   3) option to re-byte-compile modified functions if they were
  89. ;;;      byte-compiled in the first place.
  90.  
  91. (require 'backquote)
  92.  
  93. (defun insert-hooks (function-to-frob head-hook &optional tail-hook)
  94.   "If HEAD-HOOK, a symbol referring to a named hook, is non-nil, insert
  95. a form in FUNCTION-TO-FROB that will run that hook before anything
  96. else is done.
  97.  
  98. If optional TAIL-HOOK is non-nil, insert a form in FUNCTION-TO-FROB
  99. that will run the hook as the last thing done. 
  100.  
  101. Return value is new definition of FUNCTION-TO-FROB."
  102.   (let* ((func (symbol-function function-to-frob))
  103.          (function-interactivep (commandp func))
  104.          ;; Three possible results for function-interactive-form.  If
  105.          ;; function-interactivep is `t', then function-interactive-form
  106.          ;; represents the form calling `interactive' without any arguments.
  107.          ;; Since commandp returns the interactive form if there's an argument,
  108.          ;; function-interactive-form gets set to whatever commandp returns,
  109.          ;; e.g. (interactive "P")
  110.          (function-interactive-form
  111.           (if (eq t function-interactivep)
  112.               '(interactive)
  113.             function-interactivep))
  114.          ;; temp. variable to hold partial construction of new function.
  115.          function-precursor)  
  116.     (if (subrp func)
  117.         ;; If function is a subr, define a wrapper around the subr to
  118.         ;; do hooks. 
  119.         (progn
  120.           (if tail-hook
  121.               ;; Insert form to run subr, saving return value in the process.
  122.               ;; Run tail-hook, then return value from subr.
  123.               (fset 'function-precursor
  124.                     ;; Must also preserve state of interactive ability
  125.                     (if function-interactivep
  126.                         (` (let ((return-value 
  127.                                   ;; Call subr interactively if this function
  128.                                   ;; was called interactively (keep in mind
  129.                                   ;; this form is being inserted into the new
  130.                                   ;; function, not eval'ed in insert-hooks!)
  131.                                   (if (interactive-p)
  132.                                       (call-interactively (, func))
  133.                                     (apply (, func) args))))
  134.                              (run-hooks '(, tail-hook))
  135.                              return-value))
  136.                       ;; never interactive
  137.                       (` (let ((return-value (apply (, func) args)))
  138.                            (run-hooks '(, tail-hook))
  139.                            return-value))))
  140.             ;; No tail hook provided, so just apply function (no need to save
  141.             ;; return values or run hooks in this case)
  142.             (fset 'function-precursor
  143.                   ;; preserve interactiveness
  144.                   (if function-interactivep
  145.                       (` (if (interactive-p)
  146.                              (call-interactively (, func))
  147.                            (apply (, func) args)))
  148.                     ;; never interactive
  149.                     (` (apply (, func) args)))))
  150.           ;; Now finish constructing function
  151.           (if head-hook
  152.               (fset function-to-frob
  153.                     ;; If head-hook is defined, insert form for
  154.                     ;; running run-hooks.
  155.                     (` (lambda (&rest args)
  156.                          (, (documentation func))      ;might be nil
  157.                          (, function-interactive-form) ;might also be nil
  158.                          (run-hooks '(, head-hook))
  159.                          (, (symbol-function 'function-precursor)))))
  160.             (fset function-to-frob 
  161.                   (` (lambda (&rest args)
  162.                        (, (documentation func))       ;might be nil
  163.                        (, function-interactive-form)  ;might also be nil
  164.                        (, (symbol-function 'function-precursor)))))))
  165.       ;; func not subr
  166.       (let ((args (car (cdr func)))
  167.             doc 
  168.             body)
  169.         (setq doc (car (nthcdr 2 func)))
  170.         (if (stringp doc)
  171.             (setq body (nthcdr 3 func))
  172.           (setq doc nil
  173.                 body (nthcdr 2 func)))
  174.         ;; remove function-interactive-form from body since body will not be a
  175.         ;; top-level form anymore anyway (the interactive-form will be
  176.         ;; re-inserted at the top-level elsewhere).
  177.         (and function-interactive-form
  178.              (setq body (delq function-interactive-form body)))
  179.         (if tail-hook
  180.             (fset 'function-precursor
  181.                   ;; extra parens around form ((let ...)) so that below, when
  182.                   ;; we evaluate (,@ (symbol-function 'function-precursor)), we
  183.                   ;; get the proper form. 
  184.                   (` ((let ((return-value (progn (,@ body))))
  185.                        (run-hooks '(, tail-hook))
  186.                        return-value))))
  187.           (fset 'function-precursor body))
  188.         (if head-hook
  189.             (fset function-to-frob 
  190.                   (` (lambda (, args)
  191.                        (, doc)          ;might be nil
  192.                        (, function-interactive-form) ;might also be nil
  193.                        (run-hooks '(, head-hook))
  194.                        (,@ (symbol-function 'function-precursor)))))
  195.           (fset function-to-frob
  196.                 (` (lambda (, args)
  197.                      (, doc)            ;might be nil
  198.                      (, function-interactive-form) ;might also be nil
  199.                      (,@ (symbol-function 'function-precursor))))))))))
  200.  
  201. ;;; eof
  202.