[slime-devel] find-file-at-point on logical pathnames

Håkon Alstadheim hakon at alstadheim.priv.no
Tue Nov 16 01:10:38 UTC 2004


I have a hack on ffap that I did just now. Needs more testing, but its 
so simple and so convenient that I Have to share it right away. Stick 
this in your .emacs file if you want to evaluate it. First, I customize 
the ffap-string-at-point variable and create a helper function. Then a 
doctored version of ffap-file-at-point.

--- elisp code follows ---
;; File name constituents for lisp-mode
;; the string constituents could probably use some adjustment
(setq ffap-string-at-point-mode-alist
    (cons '(lisp-mode "--:;$+<>@-Z_a-z~" "" "") 
ffap-string-at-point-mode-alist ))

(defun ffap-slime-file (name)
  "Return unix version of lisp file NAME if slime
is running and we are in a lisp buffer, and unix-file
exists.  Else return nil"
  (and
   (eql major-mode 'lisp-mode)
   (fboundp 'slime-connected-p)
   (slime-connected-p)
   (slime-eval `(cl:ignore-errors
                 (cl:or
                   (cl:and
                    (cl:probe-file ,name) (ext:unix-namestring ,name))
                   (cl:and
                    (cl:probe-file (cl:concatenate 'cl:string ,name 
".lisp"))
                    (ext:unix-namestring (cl:concatenate 'cl:string 
,name ".lisp"))))))))

;; The following hacked version of ffap-file-at-point has ONE line 
changed, see comment
;; at the line
(defun ffap-file-at-point nil
  "Return filename from around point if it exists, or nil.
Existence test is skipped for names that look remote.
If the filename is not obvious, it also tries `ffap-alist',
which may actually result in an url rather than a filename."
  ;; Note: this function does not need to look for url's, just
  ;; filenames.  On the other hand, it is responsible for converting
  ;; a pseudo-url "site.com://path" to an ftp path
  (let* ((case-fold-search t)        ; url prefixes are case-insensitive
     (data (match-data))
     (string (ffap-string-at-point)) ; uses mode alist
     (name
      (or (condition-case nil
          (and (not (string-match "//" string)) ; foo.com://bar
               (substitute-in-file-name string))
        (error nil))
          string))
     (abs (file-name-absolute-p name))
     (default-directory default-directory))
    (unwind-protect
    (cond
     ;; Immediate rejects (/ and // are too common in C++):
        
         ((member name '("" "/" "//" ".")) nil)
         ((and (not abs) (ffap-slime-file name)))    ;; This line is NEW 
NEW NEW
         ;; Immediately test local filenames.  If default-directory is
         ;; remote, you probably already have a connection.
         ((and (not abs) (ffap-file-exists-string name)))
         ;; Try stripping off line numbers; good for compilation/grep 
output.
         ((and (not abs) (string-match ":[0-9]" name)
               (ffap-file-exists-string (substring name 0 
(match-beginning 0)))))
     ;; Immediately test local filenames.  If default-directory is
     ;; remote, you probably already have a connection.
     ((and (not abs) (ffap-file-exists-string name)))
     ;; Accept remote names without actual checking (too slow):
     ((if abs
          (ffap-file-remote-p name)
        ;; Try adding a leading "/" (common omission in ftp paths):
        (and
         ffap-ftp-sans-slash-regexp
         (string-match ffap-ftp-sans-slash-regexp name)
         (ffap-file-remote-p (concat "/" name)))))
     ;; Ok, not remote, try the existence test even if it is absolute:
     ((and abs (ffap-file-exists-string name)))
     ;; If it contains a colon, get rid of it (and return if exists)
     ((and (string-match path-separator name)
           (setq name (ffap-string-at-point 'nocolon))
           (ffap-file-exists-string name)))
     ;; File does not exist, try the alist:
     ((let ((alist ffap-alist) tem try case-fold-search)
        (while (and alist (not try))
          (setq tem (car alist) alist (cdr alist))
          (if (or (eq major-mode (car tem))
              (and (stringp (car tem))
               (string-match (car tem) name)))
          (and (setq try
                 (condition-case nil
                 (funcall (cdr tem) name)
                   (error nil)))
               (setq try (or
                  (ffap-url-p try) ; not a file!
                  (ffap-file-remote-p try)
                  (ffap-file-exists-string try))))))
        try))
     ;; Alist failed?  Try to guess an active remote connection
     ;; from buffer variables, and try once more, both as an
     ;; absolute and relative path on that remote host.
     ((let* (ffap-rfs-regexp    ; suppress
         (remote-dir
          (cond
           ((ffap-file-remote-p default-directory))
           ((and (eq major-mode 'internal-ange-ftp-mode)
             (string-match "^\\*ftp \\(.*\\)@\\(.*\\)\\*$"
                       (buffer-name)))
            (concat "/" (substring (buffer-name) 5 -1) ":"))
           ;; This is too often a bad idea:
           ;;((and (eq major-mode 'w3-mode)
           ;;       (stringp url-current-server))
           ;; (host-to-ange-path url-current-server))
           )))
        (and remote-dir
         (or
          (and (string-match "\\`\\(/?~?ftp\\)/" name)
               (ffap-file-exists-string
            (ffap-replace-path-component
             remote-dir (substring name (match-end 1)))))
          (ffap-file-exists-string
           (ffap-replace-path-component remote-dir name))))))
     )
      (set-match-data data))))

--- elisp code ends ---
The one point agains doing something like this is response time. If 
something like this is desired, I'll see about maybe doing it a bit more 
elegantly with an around method, althoug I don't quite see how to do 
that without worsening the response time even further. Would a custom 
function on the lisp side help speed things up?





More information about the slime-devel mailing list