defhook

Homepage: https://github.com/neil-smithline-elisp/defhook

Author: Neil Smithline

Updated:

Summary

Declare hook functionality rather dynamically implementing it

Commentary

`defhook' provides a declarative mechanism for defining
functionality for hooks. For example:

    (defhook ignore-case (dired-mode-hook) 
      "Always ignore case in `dired-mode' searches."
      (make-local-variable 'case-fold-search)
      (setq case-fold-search t))

Especially for new elispers, `defhook' provides hook manipulation
functionality in a simple and familiar (ie: `defhook' looks like
`defun') package.

`defhook' also encourages the use of multiple, well-documented hook
functions for one hook rather than a single function for that hook.

The simpler syntax and the smaller granularity of hook
functionality makes allows hook functions to be copied-and-pasted
-- even among non-elisp programmers.

Other than `defhook', hook functionality is generally written in
one of two ways: using functions or using `lambda's. An example of
a function to implement the case folding in `dired' buffers is:

     (defun buffer-case-fold-search ()
      "Always ignore case for searches in this buffer.
      (make-local-variable 'case-fold-search)
      (setq case-fold-search t))
       
     (add-hook 'dired-mode-hook #'buffer-case-fold-search)

The readibility and maintainability of hook functionality using
`lambda's quickly degrades as the functionality requires more code
to be implemented. For example, using a `lambda' to implement the
above `dired-mode-hook' functionality looks like:

     (add-hook 'dired-mode-hook
         #'(lambda ()
            (make-local-variable 'case-fold-search)
            (setq case-fold-search t))


`defhook's declaration has additional functionality:
     - Local hook functionality:
         (defhook ignore-case (dired-mode-hook :local t) ...)

     - Appending function to a hook rather than prepending it:
         (defhook ignore-case (dired-mode-hook :append t) ...)

     - Incorporation of `eval-after' functionality:
         (defhook ignore-case (dired-mode-hook :eval-after 'dired-mode) ...)

     - By default, `defhook' functions are declared to be
         `interactive' so that they can be called for debugging or
         other reasons. This can be disabled by:
         (defhook ignore-case (dired-mode-hook :interactive-spec nil) ...)

     - `defhook' helps to prevent typos by validating that the hook
         actually exists and generating an error if not. This can
         be disabled with:
         (defhook ignore-case (dired-mode-hook :validate-hook-name nil) ...)

     - While most hooks take no arguments, some do. `defhook'
         allows you to declare parameters for hook functions with:
         (defhook something-or-other
                 (window-scroll-function
                     :hook-args (window new-display-start) ...)

     - `defhook' functions can be "commented out" by using the :op
         keyword:
         (defhook ignore-case (dired-mode-hook :op no-op) ...)

     - While not a common operation, removing a specific hook can
         sometimes be difficult. `defhook' will automatically remove
         a hook function by evalating:
         (defhook ignore-case (dired-mode-hook :op delete) ...)

     - `defhook' has a series of informational messages that can be
         controlled by customization settings.
 
     - The naming functions generated by `defhook' are controlled
          by the package customization options and are intended to
          be self-documenting as well as easy to access via various
          Emacs' help functions. The value of my `dired-mode-hook'
          is:
         '(ngs:dired-mode-hook:my-key-bindings
           ngs:dired-mode-hook:ignore-case obof-inhibit-pop-up-windows
           obof-inhibit-frame-creation)
          
         FYI: "ngs" are my initials.

Reverse dependencies