git.fiddlerwoaroof.com
Raw Blame History
;;; -*- Mode: LISP; Syntax: COMMON-LISP; Package: LW-ADD-ONS; Base: 10 -*-
;;; $Header: /usr/local/cvsrep/lw-add-ons/editor.lisp,v 1.47 2015/03/06 12:54:25 edi Exp $

;;; Copyright (c) 2005-2015, Dr. Edmund Weitz.  All rights reserved. 

;;; Redistribution and use in source and binary forms, with or without
;;; modification, are permitted provided that the following conditions
;;; are met:

;;;   * Redistributions of source code must retain the above copyright
;;;     notice, this list of conditions and the following disclaimer.

;;;   * Redistributions in binary form must reproduce the above
;;;     copyright notice, this list of conditions and the following
;;;     disclaimer in the documentation and/or other materials
;;;     provided with the distribution.

;;; THIS SOFTWARE IS PROVIDED BY THE AUTHOR 'AS IS' AND ANY EXPRESSED
;;; OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
;;; WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
;;; ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
;;; DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
;;; DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
;;; GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
;;; INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
;;; WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
;;; NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
;;; SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

(in-package :lw-add-ons)

(defun symbol-string-at-point (&key (point (current-point)) previous)
  "Returns two values - a string denoting the symbol at POINT and
the package to use at that POINT if the symbol isn't
package-qualified.  PREVIOUS controls whether to look at the
previous symbol if POINT is between two symbols."
  (ignore-errors*
    (let ((string (editor::read-symbol-from-point :point point
                                                  :read-package-name t
                                                  :previous previous))
          (package (editor::buffer-package-to-use point)))
      (values string package))))

(defun symbol-at-point (&key (point (current-point)) previous)
  "Returns the symbol at POINT.  PREVIOUS controls whether to
look at the previous symbol if POINT is between two symbols."
  (ignore-errors*
    (multiple-value-bind (string package)
        (symbol-string-at-point :point point :previous previous)
      (let* ((*package* package)
             (candidate (read-from-string string)))
        (and (symbolp candidate)
             candidate)))))

(defun enclosing-operators ()
  "Returns a list of potential operators \(symbols behind an
opening parenthesis) starting from point and going up backwards."
  (save-excursion
    (loop while (ignore-errors*
                  (backward-up-list-command 1) t)
          when (when (looking-at "\(")
                 (forward-character-command 1)
                 (prog1
                   (symbol-at-point)
                   (forward-character-command -1)))
          collect it)))

(defun show-info (info &key full-length-p)
  "Shows the string INFO in the echo area.  Shows no more than
*MAX-INFO-LENGTH* unless FULL-LENGHT-P is true."
  (apply #'message
         (if (and (not full-length-p)
                  (> (length info) *max-info-length*))
           (list "~A [...]" (subseq info 0 *max-info-length*))
           (list "~A" info))))

(defun show-arglist ()
  "Shows the argument list of the nearest enclosing operator that
has a function definition in the echo arrea.  Shows the doc
string as well unless *SHOW-DOC-STRING-WHEN-SHOWING-ARGLIST* is
NIL."
  (when-let (object
             (loop for operator in (enclosing-operators)
                   when (and (symbolp operator)
                             (fboundp operator))
                   do (return operator)))
    (show-info (format nil "~A~@[~%~A~]"
                       (cons object (function-lambda-list object))
                       (and *show-doc-string-when-showing-arglist*
                            (documentation object 'function))))))

(defun completions-for-echo-area (completions)
  "Returns a string which shows a two-column list of the elements
\(which should be strings) in COMPLETIONS but no more than
*MAX-COMPLETIONS-TO-SHOW* of them."
  (let ((max-left-width
         (loop for completion in completions
               for i from 1 to *max-completions-to-show*
               when (oddp i)
               maximize (length completion))))
    (with-output-to-string (out)
      (format out "~&Possible completions:~%~%")
      (loop for (completion-1 completion-2 . rest) on completions by #'cddr
            for i from 1 to *max-completions-to-show* by 2
            do (format out "~&~VA  ~A" max-left-width
                       completion-1
                       (cond ((and rest
                                   (>= (1+ i) *max-completions-to-show*))
                              "[...]")
                             (completion-2)
                             (t "")))))))

(defun char-before ()
  "Returns the character before the current point."
  (character-at (current-point) -1))

(defun maybe-insert-right-parenthesis ()
  "If the symbol at or before point is in function position and
denotes a function with an empty lambda list inserts a right
parenthesis, otherwise inserts a space and show the argument list
in the echo area."
  (when-let (symbol (symbol-at-point :previous t))
    (when (and (save-excursion
                 (backward-form-command 1)
                 (eql (char-before) #\())
               (symbolp symbol)
               (fboundp symbol))
      (cond ((null (function-lambda-list symbol))
             (self-insert-command 1 #\)))
            ((not (looking-at " "))
             (insert-space-and-show-arglist-command nil))))))

#-:editor-has-dont-undo
(defmacro without-undo-with-cleanups (buffer form &body cleanups)
  "Editor utility macro.  See source code for LW editor."
  (lw:rebinding (buffer)
    (lw:with-unique-names (was-recording)
      `(let ((,was-recording (editor::check-set-buffer-without-undo ,buffer)))
         (unwind-protect
             ,form
           (when ,was-recording
             (editor::set-buffer-flag-bit ,buffer editor::*buffer-flag-dont-record-undo* nil))
           ,@cleanups)))))

#-:editor-has-dont-undo
(defmacro recording-for-undo-internal (point1 point2 line-start-p &body body)
  "Does the whole work for RECORDING-FOR-UNDO.  See source code for LW editor."
  (lw:with-unique-names (old-string start end want-undo before-modified buffer-sym)
    (lw:rebinding (point1 point2)
      `(let* ((,buffer-sym (point-buffer ,point1))
              (,want-undo (editor::check-want-to-record-undo-p ,buffer-sym nil))
              (,before-modified (editor::buffer-modified-tick ,buffer-sym))
              (,start (when ,want-undo 
                         (let ((lsp ,line-start-p)
                               (sp (copy-i-point ,point1 :before-insert)))
                           (if lsp 
                             (line-start sp))
                           sp)))
              (,end (when ,want-undo (copy-i-point ,point2 :after-insert)))
              (,old-string (when ,want-undo
                             (editor::points-to-buffer-string ,start ,end))))
         (without-undo-with-cleanups ,buffer-sym
             (progn ,@body)
           (when ,want-undo
             (editor::record-replace-region ,start ,end ,old-string ,before-modified)
             (editor::delete-it ,start)
             (editor::delete-it ,end)))))))

#-:editor-has-dont-undo
(defmacro recording-for-undo (point1 point2 &body body)
  "Performs code in BODY and records changes between POINT1 and
POINT2 for undo operation.  See source code for LW editor."
  `(recording-for-undo-internal ,point1 ,point2 nil ,@body))

#-:editor-has-dont-undo
(defmacro recording-for-undo-locking (point1 point2 &body body)
  "Like RECORDING-FOR-UNDO, but with lock.  See source code for LW editor."
  (lw:rebinding (point1)
    #-:editor-does-not-have-with-buffer-locked
    `(with-buffer-locked ((point-buffer ,point1))
       (recording-for-undo ,point1 ,point2 ,@body))
    #+:editor-does-not-have-with-buffer-locked
    `(editor::with-locked-buffer (point-buffer ,point1)
       (recording-for-undo ,point1 ,point2 ,@body))))

#+:editor-has-dont-undo
(defmacro recording-for-undo (point1 point2 &body body)
  "Performs code in BODY and records changes between POINT1 and
POINT2 for undo operation.  See source code for LW editor."
  (lw:with-unique-names (old-string start end dont changed)
    (lw:rebinding (point1 point2)
      `(let* ((,dont editor::*dont-undo*)
              (,changed (buffer-modified (point-buffer ,point1)))
              (,start (unless ,dont (copy-i-point ,point1 :before-insert)))
              (,end (unless ,dont (copy-i-point ,point2 :after-insert)))
              (,old-string (unless ,dont (editor::points-to-buffer-string ,point1 ,point2))))
         (unwind-protect
             (let ((editor::*dont-undo* t))
               ,@body)
           (progn
             (unless ,dont
               (editor::record-replace-region ,start ,end ,old-string ,changed)
               (editor::delete-it ,start)
               (editor::delete-it ,end))))))))

(defmacro recording-for-undo% (point1 point2 &body body)
  "Helper macro which dispatches to RECORDING-FOR-UNDO or
RECORDING-FOR-UNDO-LOCKING depending on the LispWorks release."
  #-:editor-has-dont-undo
  `(recording-for-undo-locking ,point1 ,point2
     ,@body)
  #+:editor-has-dont-undo
  `(recording-for-undo ,point1 ,point2
     ,@body))

(defun current-line ()
  "Returns the line the point is currently on as a string."
  (line-string (current-point)))

(defun can-move-upwards-p ()
  "Returns true if it is possible to move backward up from the
current point."
  (save-excursion
    (with-point ((point (current-point)))
      (backward-up-list-command 1)
      (point< (current-point) point))))

(defadvice (editor:find-alternate-file-command change-prompt :around
                                               :documentation "Makes
sure FIND-ALTERNATE-FILE-COMMAND provides the full pathname of the
current buffer as the default when prompting.")
    (p &optional pathname (buffer (current-buffer)))
  (let ((*change-default-for-file-prompt* t))
    (call-next-advice p pathname buffer)))

(defadvice (editor:find-alternate-file-command refresh :after
                                               :documentation "After
FIND-ALTERNATE-COMMAND has run makes sure the contents of the buffer
are consistent with the file on disk.")
    (p &optional pathname (buffer (current-buffer)))
  (declare (ignore p pathname))
  (let ((pathname (buffer-pathname buffer)))
    (unless (check-disk-version-consistent pathname buffer)
      (let* ((tn (probe-file pathname))
             (pn (or tn (editor::canonical-pathname pathname))))
        (editor::read-da-file pn tn buffer)))))

(defadvice (editor:prompt-for-file change-prompt :around
                                   :documentation "When DEFAULT-STRING
is NIL, DEFAULT is a pathname, and *CHANGE-DEFAULT-FOR-FILE-PROMPT* is
true sets the full namestring of DEFAULT to be the default string.")
    (&rest rest &key default default-string &allow-other-keys)
  (let ((default-string (cond (default-string)
                              ((and *change-default-for-file-prompt*
                                    (pathnamep default))
                               (namestring default))
                              ((pathnamep default)
                               (namestring (pathname-location default)))
                              (t default))))
    (apply #'call-next-advice
           :default-string default-string
           rest)))
                                        
(defadvice (editor::find-pattern region-only :around
                                 :documentation "Searches only up
until *SEARCH-END* unless the value of this variable is NIL.")
    (point pattern &optional limit)
  (cond ((and (null limit)
              *search-end*)
         (call-next-advice point pattern *search-end*))
        (t (call-next-advice point pattern limit))))

#-:editor-does-not-have-i-find-pattern
(defadvice (editor::i-find-pattern region-only :around
                                   :documentation "Searches only up
until *SEARCH-END* unless the value of this variable is NIL.")
    (point pattern &optional limit)
  (cond ((and (null limit)
              *search-end*)
         (call-next-advice point pattern *search-end*))
        (t (call-next-advice point pattern limit))))

(defadvice (editor::query-replace-string region-only :around
                                         :documentation "Performs
operation only up until *SEARCH-END* unless the value of this variable
is NIL.  Also makes sure that all replacements can be undone with one
undo command.")
    (&rest rest &key (point (current-point)) &allow-other-keys)
  (let* ((current-mark (and (variable-value-if-bound 'editor::active-region-overlay
                                                     :buffer (current-buffer))
                            (current-mark nil t)))
         (switch-p (and current-mark
                        (point< current-mark (current-point))))
         (*search-end* (and current-mark
                            (copy-point (cond (switch-p (current-point))
                                              (t current-mark)))))
         (start (cond ((and current-mark switch-p)
                       current-mark)
                      (current-mark (current-point))
                      (t point))))
    (unwind-protect
        (with-point ((%start start)
                     (%end (or *search-end*
                               (current-point))))
          (unless *search-end*
            (editor:buffer-end %end))
          #+:editor-has-dont-undo
          (recording-for-undo %start %end
            (apply #'call-next-advice :point start rest))
          ;; in new LispWorks versions it is no longer necessary to
          ;; record for undo here
          #-:editor-has-dont-undo          
          (apply #'call-next-advice :point start rest))
      (when *search-end*
        (delete-point *search-end*)))))

(defadvice (editor::find-next-ordinary-window allow-listener :around
                                              :documentation "Allows
the \"Next Ordinary Window\" command to switch to a listener window.")
    (current-window)
  (let ((*forbidden-buffers* (remove :listener *forbidden-buffers*)))
    (call-next-advice current-window)))

#+:editor-does-not-have-go-back
(defun push-onto-definitions-stack ()
  "Pushes current point onto *FIND-DEFINITIONS-STACK* unless the
buffer isn't selectable."
  (unless (editor::forbidden-buffer-p (current-buffer))
    (push (copy-point (current-point))
          *find-definitions-stack*)))

#+:editor-does-not-have-go-back
(defadvice (find-source-command push-onto-definitions-stack :around
                                :documentation "Pushes current point
onto *FIND-DEFINITIONS-STACK*.")
    (&rest args)
  (push-onto-definitions-stack)
  (apply #'call-next-advice args))

#+:editor-does-not-have-go-back
(defadvice (find-source-for-dspec-command push-onto-definitions-stack :around
                                          :documentation "Pushes
current point onto *FIND-DEFINITIONS-STACK*.")
    (&rest args)
  (push-onto-definitions-stack)
  (apply #'call-next-advice args))

#+:editor-does-not-have-go-back
(defadvice (find-command-definition-command push-onto-definitions-stack :around
                                            :documentation "Pushes
current point onto *FIND-DEFINITIONS-STACK*.")
    (&rest args)
  (push-onto-definitions-stack)
  (apply #'call-next-advice args))

#+:editor-does-not-have-go-back
(defadvice (editor::edit-callers-command push-onto-definitions-stack :around
                                         :documentation "Pushes
current point onto *FIND-DEFINITIONS-STACK*.")
    (&rest args)
  (push-onto-definitions-stack)
  (apply #'call-next-advice args))

#+:editor-does-not-have-go-back
(defadvice (editor::edit-callees-command push-onto-definitions-stack :around
                                         :documentation "Pushes
current point onto *FIND-DEFINITIONS-STACK*.")
    (&rest args)
  (push-onto-definitions-stack)
  (apply #'call-next-advice args))

#+:editor-does-not-have-go-back
(defadvice (find-tag-command push-onto-definitions-stack :around
                             :documentation "Pushes current point onto
*FIND-DEFINITIONS-STACK*.")
    (&rest args)
  (push-onto-definitions-stack)
  (apply #'call-next-advice args))

#+:editor-does-not-have-go-back
(defadvice (tags-search-command push-onto-definitions-stack :around
                                :documentation "Pushes current point
onto *FIND-DEFINITIONS-STACK*.")
    (&rest args)
  (push-onto-definitions-stack)
  (apply #'call-next-advice args))

#+:editor-does-not-have-go-back
(defadvice (continue-tags-search-command push-onto-definitions-stack :around
                                         :documentation "Pushes
current point onto *FIND-DEFINITIONS-STACK*.")
    (&rest args)
  (when editor::*meta-comma-action*
    (push-onto-definitions-stack))
  (apply #'call-next-advice args))

(defun complete-system (string parse-inf)
  "Completion function used by PROMPT-FOR-ASDF-SYSTEM."
  (declare (ignore parse-inf))
  (editor::complete-string string *all-asdf-systems*
                           :ignore-case t))

(defun prompt-for-asdf-system (string &optional prompt help no-check)
  "Prompts for an ASDF system name with STRING being the default."
  (let ((*all-asdf-systems* (list-asdf-systems)))
    (editor::parse-for-something
     :prompt (or prompt "ASDF system: ")
     :must-exist t
     :help (or help "Type a name of an ASDF system.")
     :default (or string "")
     :default-string (or string "")
     :verify-func (if no-check
                    (lambda (string parse-inf)
                      (declare (ignore parse-inf))
                      string)
                    (lambda (string parse-inf)
                      (declare (ignore parse-inf))
                      (and (find string *all-asdf-systems* :test #'string-equal)
                           string)))
     :type :string
     :default-in-prompt nil
     :complete-func 'complete-system)))

(defun prompt-for-asdf-system-with-default (&optional prompt help no-check)
  "Prompts for an ASDF system name and tries to find a default in the
default directory of the current buffer."
  (let* ((directory (editor::buffer-default-directory (editor:current-buffer)))
         (candidate (first (directory (make-pathname :name nil
                                                     :type "asd"
                                                     :defaults directory))))
         (default (and candidate
                       (pathname-name candidate))))
    (prompt-for-asdf-system default prompt help no-check)))

(defun complete-shortcut (string parse-inf)
  "Completion function used by PROMPT-FOR-LISTENER-SHORTCUT."
  (declare (ignore parse-inf))
  (editor::complete-string string (mapcar #'cdr *listener-shortcuts*)
                           :ignore-case t))

(defun find-full-name (abbrev)
  "Given an abbreviation finds the first item in *LISTENER-SHORTCUTS*
that is named by this abbreviation."
  (or (loop for (short . long) in *listener-shortcuts*
            when (string-equal short abbrev)
            do (return long))
      (loop for (nil . long) in *listener-shortcuts*
            when (starts-with-p long abbrev)
            do (return long))))

(defun prompt-for-listener-shortcut ()
  "Prompts for a listener shortcut."
  (let ((input
         (editor::parse-for-something
          :prompt (format nil "Shortcut [~{~A~^,~}] or Command: "
                          (sort (mapcar #'car *listener-shortcuts*) #'string-lessp))
          :must-exist t
          :help (format nil "Type the name or abbreviation of a listener shortcut:~%~%~{~A: ~A~%~}"
                        (loop for (short . long) in *listener-shortcuts*
                              collect short
                              collect long))
          :default ""
          :default-string ""
          :verify-func (lambda (string parse-inf)
                         (declare (ignore parse-inf))
                         (and (find-full-name string)
                              string))
          :type :string
          :default-in-prompt nil
          :complete-func 'complete-shortcut)))
    (find-full-name input)))

(eval-when (:compile-toplevel :load-toplevel :execute)
  (let ((*handle-warn-on-redefinition* :quiet))
    (defmacro with-input-from-region ((var start end) &body body &environment env)
      "During the evaluation of BODY, VAR is bound to a stream which
returns input from the region denoted by START and END."
      (multiple-value-bind (forms decls)
          (dspec:separate-declarations body env)
        `(let ((,var (pop editor::*free-editor-region-streams*)))
           ,@decls
           (setq ,var 
                 (if ,var
                   (editor::modify-editor-region-stream ,var ,start ,end)
                   (editor::make-editor-region-stream ,start ,end)))
           (unwind-protect
               (progn ,@forms)
             (editor::free-region-stream ,var)))))))

(defmacro with-compilation-environment-at-point ((point &key (compilep nil)
                                                             start-message
                                                             end-message)
                                                        &body body)
  (with-unique-names (buffer)
    `(editor::with-compilation-environment-at-point-fn
      ,point ,start-message ,end-message
      #'(lambda (,buffer)
          (let* ((,(if compilep '*compile-file-pathname* '*load-pathname*)
                  (buffer-pathname ,buffer))
                 (,(if compilep '*compile-file-truename* '*load-truename*)
                  (buffer-pathname ,buffer)) ; buffer-pathname _is_ a truename
                 )
            ,@body)))))

(defun returning-lisp-eval (buffer start end print)
  "Evaluates the region in the buffer BUFFER which is denoted by
START and END and returns the result."
  (with-compilation-environment-at-point (start :start-message "Evaluating..."
                                                :end-message (and (not (editor::windowp print))
                                                                  "Finished evaluating"))
    (with-input-from-region (stream start end)
      (let ((out-stream (if (streamp print)
                          print
                          (editor::buffer-stream buffer)))
            return-value)
        (handler-case
            (progn
              (common-utilities:load-text-stream
               stream
               :exit-load-p t
               :eval-function #'(lambda (form)
                                  (multiple-value-list
                                    (editor::editor-eval buffer form)))
               :print-function #'(lambda (result)
                                   (setq return-value result)
                                   (and print
                                        (if (editor::windowp print)
                                          (process-character
                                           `(message ,editor::*values-format-string* ,result)
                                           print)
                                          (editor::in-output-eval-results out-stream result)))))
              return-value)
          (end-of-file (x)
            (editor::report-region-lisp-eval-error "Incomplete S-expression in region " x)
            (return-from returning-lisp-eval nil))
          (reader-error (x)
            (editor::report-region-lisp-eval-error "Error while reading: ~a " x)
            (return-from returning-lisp-eval nil)))))))

(defmacro with-output-to-help-window ((stream &rest options) &body body)
  "Executes BODY with output that goes to STREAM redirected to an
IDE help window."
  `(editor::with-output-to-help-window-1
       #'(lambda (,stream) ,@body)
     ,@options))

(defun complete-package-name (string parse-inf)
  "Like the function of the same name in the EDITOR package, but
case-insensitive."
  (declare (ignore parse-inf))
  (editor::complete-string
   string
   (sort (loop for pkg in (list-all-packages)
               append (cons (package-name pkg) (package-nicknames pkg)))
         'string<)
   :ignore-case t))


(defun verify-package-func (string parse-inf)
  "Like the function of the same name in the EDITOR package, but
case-insensitive."
  (declare (type editor::parse-inf parse-inf))
  (or (find-package (ignore-errors* (read-from-string (string-upcase string))))
      (if (and (parse-inf-must-exist parse-inf)
	       (not (editor::recursive-parse 'prompt-for-y-or-n
                                             :prompt 
                                             "No such package.  Create it?")))
	  (values nil :no-value)
	(make-package string))))

(defun prompt-for-package* (&key (must-exist  t)
                                 (default *package*)
                                 (prompt  "package: ")
                                 (help  "Type a package name.")
                                 &allow-other-keys)
  "Like EDITOR:PROMPT-FOR-PACKAGE, but case-insensitive."
  (editor::parse-for-something :prompt prompt
                               :must-exist must-exist
                               :help help
                               :default default
                               :verify-func  'verify-package-func
                               :type :keyword
                               :complete-func 'complete-package-name
                               :default default))

(defun clean-namestring (namestring)
  "Replaces characters in NAMESTRING which are illegal for a filename
with underlines.  This function is aimed at Microsoft Windows but
shouldn't do any harm on OS X or Linux."
  (regex-replace-all "[\\\\/?*:<>|\"\\000-\\037]" namestring "_"))

(defun normalize-pathname-for-backup (pathname)
  "Converts the full form of the pathname designator PATHNAME to a
string that is suitable \(modulo illegal characters) as the NAME
component of a filename.  This is a simplified form of what GNU Emacs
does."
  (regex-replace-all "[/\\\\]" 
                     (regex-replace "^([a-zA-Z]):[/\\\\]"
                                    (namestring pathname)
                                    "!drive_\\1!")
                     "!"))

(defun make-backup-filename-using-backup-directory (pathname)
  "Creates and returns a backup pathname for PATHNAME.  Assumes that
*BACKUP-DIRECTORY* denotes a directory.  Note that due to the way the
backup pathname is constructed it is possible that two different files
end up with the same backup filename!"
  (ensure-directories-exist
   (make-pathname :name (clean-namestring
                         (normalize-pathname-for-backup pathname))
                  :type nil
                  :version nil
                  :defaults *backup-directory*)))

(defadvice (editor::make-backup-filename alternative-location :around
                                         :documentation "Circumvents
the original function if the variable *MAKE-BACKUP-FILENAME-FUNCTION*
specifies another function to be used instead.")
    (pathname)
  (cond (*make-backup-filename-function*
         (funcall *make-backup-filename-function* pathname))
        (t (call-next-advice pathname))))