| |
| |
| |
| |
| |
|
|
| (cl:in-package #:hooks) |
|
|
| |
|
|
| (defmethod hook-combination ((hook t)) |
| 'cl:progn) |
|
|
| (defmethod add-to-hook ((hook t) (handler function) |
| &key |
| (duplicate-policy :replace)) |
| (let+ ((present? (member handler (hook-handlers hook))) |
| ((&flet add-it () |
| (push handler (hook-handlers hook)) |
| handler))) |
| (ecase duplicate-policy |
| |
| (:do-nothing |
| (if present? |
| (values (hook-handlers hook) t) |
| (values (add-it) nil))) |
|
|
| |
| |
| (:add |
| (values (add-it) present?)) |
|
|
| |
| |
| |
| (:replace |
| (when present? |
| |
| |
| (removef (hook-handlers hook) handler)) |
| (values (add-it) present?)) |
|
|
| |
| |
| (:error |
| (when present? |
| (error 'duplicate-handler |
| :hook hook |
| :handler handler)) |
| (values (add-it) nil))))) |
|
|
| (defmethod remove-from-hook ((hook t) (handler function)) |
| (removef (hook-handlers hook) handler)) |
|
|
| (defmethod clear-hook ((hook t)) |
| (setf (hook-handlers hook) nil)) |
|
|
| (defmethod run-hook ((hook t) &rest args) |
| (let+ (((&structure-r/o hook- handlers combination) hook) |
| (result '()) |
| (current-handler) |
| ((&labels run-handler (handler) |
| (when-let ((values (multiple-value-list |
| (apply (the function handler) args)))) |
| (push values result)))) |
| ((&labels handle-error (condition) |
| (signal-with-hook-and-handler-restarts |
| hook current-handler condition |
| (lambda () |
| (setf handlers (hook-handlers hook) |
| result '()) |
| (throw 'abort-handler nil)) |
| (lambda (value) |
| (return-from run-hook value)) |
| (lambda () |
| (push current-handler handlers) |
| (throw 'abort-handler nil)) |
| (lambda () |
| (throw 'abort-handler nil)) |
| (lambda (value) |
| (push (list value) result) |
| (throw 'abort-handler nil)))))) |
| (declare (dynamic-extent #'run-handler #'handle-error)) |
|
|
| |
| (when handlers |
| |
| |
| |
| |
| (handler-bind ((error #'handle-error)) |
| (do () ((null handlers)) |
| (catch 'abort-handler |
| (do () ((null handlers)) |
| (setf current-handler (pop handlers)) |
| (run-handler current-handler))))) |
| (setf result (nreverse result))) |
|
|
| |
| |
| (case combination |
| (cl:progn (values-list (lastcar result))) |
| (t (combine-results hook combination result))))) |
|
|
| (declaim (inline run-hook-fast)) |
|
|
| (defun run-hook-fast (hook &rest args) |
| "Run HOOK with ARGS like `run-hook', with the following differences: |
| + do not run any methods installed on `run-hook' |
| + do not install any restarts |
| + do not collect or combine any values returned by handlers." |
| (declare (optimize (speed 3) (safety 0) (debug 0))) |
| |
| (dolist (handler (hook-handlers hook)) |
| (apply #'run-handler-without-restarts handler args)) |
| (values)) |
|
|
| (defmethod combine-results ((hook t) |
| (combination (eql 'cl:progn)) |
| (results list)) |
| (apply #'values (lastcar results))) |
|
|
| (defmethod combine-results ((hook t) |
| (combination function) |
| (results list)) |
| (apply combination (mapcar #'first results))) |
|
|