changeset 106239:0ac473df1bd1

Provide additional default values (file name at point or at the current Dired line) via M-n for file reading minibuffers. (Bug#5010) * minibuffer.el (read-file-name-defaults): New function. (read-file-name): Reset `minibuffer-default' to nil when it duplicates initial input `insdef'. Bind `minibuffer-default-add-function' to lambda that calls `read-file-name-defaults' in `minibuffer-selected-window'. (minibuffer-insert-file-name-at-point): New command. * files.el (file-name-at-point-functions): New defcustom. (find-file-default): Remove defvar. (find-file-read-args): Don't use `find-file-default'. Move `minibuffer-with-setup-hook' that sets `minibuffer-default' to `read-file-name'. (find-file-literally): Use `read-file-name' with `confirm-nonexistent-file-or-buffer'. * ffap.el (ffap-guess-file-name-at-point): New autoloaded function. * dired.el (dired-read-dir-and-switches): Move `minibuffer-with-setup-hook' that sets `minibuffer-default' to `read-file-name'. (dired-file-name-at-point): New function. (dired-mode): Add hook `dired-file-name-at-point' to `file-name-at-point-functions'.
author Juri Linkov <juri@jurta.org>
date Wed, 25 Nov 2009 17:11:29 +0000
parents 8cb41d65f821
children 16d3ef458ae1
files etc/NEWS lisp/ChangeLog lisp/dired.el lisp/ffap.el lisp/files.el lisp/minibuffer.el
diffstat 6 files changed, 136 insertions(+), 17 deletions(-) [+]
line wrap: on
line diff
--- a/etc/NEWS	Wed Nov 25 16:36:04 2009 +0000
+++ b/etc/NEWS	Wed Nov 25 17:11:29 2009 +0000
@@ -150,6 +150,10 @@
 ** Completions in the *Completions* buffer are sorted vertically
 when the value of the new variable `completions-format' is `vertical'.
 
+** M-n provides more default values in the minibuffer of commands that
+read a file and directory name: a file name at point (when ffap is loaded
+without ffap-bindings), a file name on the current line in the Dired buffer.
+
 ** M-r is bound to the new `move-to-window-line-top-bottom'
 to mirror the new behavior of C-l in Emacs-23.1.
 
--- a/lisp/ChangeLog	Wed Nov 25 16:36:04 2009 +0000
+++ b/lisp/ChangeLog	Wed Nov 25 17:11:29 2009 +0000
@@ -1,3 +1,32 @@
+2009-11-25  Juri Linkov  <juri@jurta.org>
+
+	Provide additional default values (file name at point or at the
+	current Dired line) via M-n for file reading minibuffers.  (Bug#5010)
+
+	* minibuffer.el (read-file-name-defaults): New function.
+	(read-file-name): Reset `minibuffer-default' to nil when
+	it duplicates initial input `insdef'.
+	Bind `minibuffer-default-add-function' to lambda that
+	calls `read-file-name-defaults' in `minibuffer-selected-window'.
+	(minibuffer-insert-file-name-at-point): New command.
+
+	* files.el (file-name-at-point-functions): New defcustom.
+	(find-file-default): Remove defvar.
+	(find-file-read-args): Don't use `find-file-default'.
+	Move `minibuffer-with-setup-hook' that sets `minibuffer-default'
+	to `read-file-name'.
+	(find-file-literally): Use `read-file-name' with
+	`confirm-nonexistent-file-or-buffer'.
+
+	* ffap.el (ffap-guess-file-name-at-point): New autoloaded function.
+
+	* dired.el (dired-read-dir-and-switches):
+	Move `minibuffer-with-setup-hook' that sets `minibuffer-default'
+	to `read-file-name'.
+	(dired-file-name-at-point): New function.
+	(dired-mode): Add hook `dired-file-name-at-point' to
+	`file-name-at-point-functions'.
+
 2009-11-25  Stefan Monnier  <monnier@iro.umontreal.ca>
 
 	Really make the *Completions* window soft-dedicated (bug#5030).
--- a/lisp/dired.el	Wed Nov 25 16:36:04 2009 +0000
+++ b/lisp/dired.el	Wed Nov 25 17:11:29 2009 +0000
@@ -599,12 +599,8 @@
 	    (if (next-read-file-uses-dialog-p)
 		(read-directory-name (format "Dired %s(directory): " str)
 				     nil default-directory nil)
-	      (let ((default (and buffer-file-name
-				  (abbreviate-file-name buffer-file-name))))
-		(minibuffer-with-setup-hook
-		    (lambda () (setq minibuffer-default default))
-		  (read-file-name (format "Dired %s(directory): " str)
-				  nil default-directory nil)))))))
+	      (read-file-name (format "Dired %s(directory): " str)
+			      nil default-directory nil)))))
 
 ;; We want to switch to a more sophisticated version of
 ;; dired-read-dir-and-switches like the following, if there is a way
@@ -659,6 +655,15 @@
 ;;             (read-file-name (format "Dired %s(directory): " str)
 ;;                             nil default-directory nil))))))))
 
+(defun dired-file-name-at-point ()
+  "Try to get a file name at point in the current dired buffer.
+This hook is inteneded to be put in `file-name-at-point-functions'."
+  (let ((filename (dired-get-filename nil t)))
+    (when filename
+      (if (file-directory-p filename)
+	  (file-name-as-directory (abbreviate-file-name filename))
+	(abbreviate-file-name filename)))))
+
 ;;;###autoload (define-key ctl-x-map "d" 'dired)
 ;;;###autoload
 (defun dired (dirname &optional switches)
@@ -1772,6 +1777,7 @@
   (when (featurep 'dnd)
     (set (make-local-variable 'dnd-protocol-alist)
 	 (append dired-dnd-protocol-alist dnd-protocol-alist)))
+  (add-hook 'file-name-at-point-functions 'dired-file-name-at-point nil t)
   (add-hook 'isearch-mode-hook 'dired-isearch-filenames-setup nil t)
   (run-mode-hooks 'dired-mode-hook))
 
--- a/lisp/ffap.el	Wed Nov 25 16:36:04 2009 +0000
+++ b/lisp/ffap.el	Wed Nov 25 17:11:29 2009 +0000
@@ -1893,6 +1893,29 @@
     (call-interactively 'dired-at-point)))
 
 
+;;; Hooks to put in `file-name-at-point-functions':
+
+;;;###autoload
+(progn (defun ffap-guess-file-name-at-point ()
+  "Try to get a file name at point.
+This hook is inteneded to be put in `file-name-at-point-functions'."
+  (when (fboundp 'ffap-guesser)
+    ;; Logic from `ffap-read-file-or-url' and `dired-at-point-prompter'.
+    (let ((guess (ffap-guesser)))
+      (setq guess
+	    (if (or (not guess)
+		    (and (fboundp 'ffap-url-p)
+			 (ffap-url-p guess))
+		    (and (fboundp 'ffap-file-remote-p)
+			 (ffap-file-remote-p guess)))
+		guess
+	      (abbreviate-file-name (expand-file-name guess))))
+      (when guess
+	(if (file-directory-p guess)
+	    (file-name-as-directory guess)
+	  guess))))))
+
+
 ;;; Offer default global bindings (`ffap-bindings'):
 
 (defvar ffap-bindings
--- a/lisp/files.el	Wed Nov 25 16:36:04 2009 +0000
+++ b/lisp/files.el	Wed Nov 25 17:11:29 2009 +0000
@@ -411,6 +411,14 @@
   :type '(hook :options (cvs-dired-noselect dired-noselect))
   :group 'find-file)
 
+;; FIXME: also add a hook for `(thing-at-point 'filename)'
+(defcustom file-name-at-point-functions '(ffap-guess-file-name-at-point)
+  "List of functions to try in sequence to get a file name at point.
+Each function should return either nil or a file name found at the
+location of point in the current buffer."
+  :type '(hook :options (ffap-guess-file-name-at-point))
+  :group 'find-file)
+
 ;;;It is not useful to make this a local variable.
 ;;;(put 'find-file-not-found-hooks 'permanent-local t)
 (defvar find-file-not-found-functions nil
@@ -1275,9 +1283,6 @@
     ;;(make-frame-visible (window-frame old-window))
     ))
 
-(defvar find-file-default nil
-  "Used within `find-file-read-args'.")
-
 (defmacro minibuffer-with-setup-hook (fun &rest body)
   "Add FUN to `minibuffer-setup-hook' while executing BODY.
 BODY should use the minibuffer at most once.
@@ -1298,12 +1303,7 @@
 	 (remove-hook 'minibuffer-setup-hook ,hook)))))
 
 (defun find-file-read-args (prompt mustmatch)
-  (list (let ((find-file-default
-	       (and buffer-file-name
-		    (abbreviate-file-name buffer-file-name))))
-	  (minibuffer-with-setup-hook
-	      (lambda () (setq minibuffer-default find-file-default))
-	    (read-file-name prompt nil default-directory mustmatch)))
+  (list (read-file-name prompt nil default-directory mustmatch)
 	t))
 
 (defun find-file (filename &optional wildcards)
@@ -2020,7 +2020,10 @@
 In a Lisp program, if you want to be sure of accessing a file's
 contents literally, you should create a temporary buffer and then read
 the file contents into it using `insert-file-contents-literally'."
-  (interactive "FFind file literally: ")
+  (interactive
+   (list (read-file-name
+  	  "Find file literally: " nil default-directory
+  	  (confirm-nonexistent-file-or-buffer))))
   (switch-to-buffer (find-file-noselect filename nil t)))
 
 (defvar after-find-file-from-revert-buffer nil)
--- a/lisp/minibuffer.el	Wed Nov 25 16:36:04 2009 +0000
+++ b/lisp/minibuffer.el	Wed Nov 25 17:11:29 2009 +0000
@@ -1321,6 +1321,32 @@
 (declare-function x-file-dialog "xfns.c"
                   (prompt dir &optional default-filename mustmatch only-dir-p))
 
+(defun read-file-name-defaults (&optional dir initial)
+  (let ((default
+	  (cond
+	   ;; With non-nil `initial', use `dir' as the first default.
+	   ;; Essentially, this mean reversing the normal order of the
+	   ;; current directory name and the current file name, i.e.
+	   ;; 1. with normal file reading:
+	   ;; 1.1. initial input is the current directory
+	   ;; 1.2. the first default is the current file name
+	   ;; 2. with non-nil `initial' (e.g. for `find-alternate-file'):
+	   ;; 2.2. initial input is the current file name
+	   ;; 2.1. the first default is the current directory
+	   (initial (abbreviate-file-name dir))
+	   ;; In file buffers, try to get the current file name
+	   (buffer-file-name
+	    (abbreviate-file-name buffer-file-name))))
+	(file-name-at-point
+	 (run-hook-with-args-until-success 'file-name-at-point-functions)))
+    (when file-name-at-point
+      (setq default (delete-dups
+		     (delete "" (delq nil (list file-name-at-point default))))))
+    ;; Append new defaults to the end of existing `minibuffer-default'.
+    (append
+     (if (listp minibuffer-default) minibuffer-default (list minibuffer-default))
+     (if (listp default) default (list default)))))
+
 (defun read-file-name (prompt &optional dir default-filename mustmatch initial predicate)
   "Read file name, prompting with PROMPT and completing in directory DIR.
 Value is not expanded---you must call `expand-file-name' yourself.
@@ -1404,7 +1430,24 @@
                     (lexical-let ((dir (file-name-as-directory
                                         (expand-file-name dir))))
                       (minibuffer-with-setup-hook
-                          (lambda () (setq default-directory dir))
+                          (lambda ()
+			    (setq default-directory dir)
+			    ;; When the first default in `minibuffer-default'
+			    ;; duplicates initial input `insdef',
+			    ;; reset `minibuffer-default' to nil.
+			    (when (equal (or (car-safe insdef) insdef)
+					 (or (car-safe minibuffer-default)
+					     minibuffer-default))
+			      (setq minibuffer-default
+				    (cdr-safe minibuffer-default)))
+			    ;; On the first request on `M-n' fill
+			    ;; `minibuffer-default' with a list of defaults
+			    ;; relevant for file-name reading.
+			    (set (make-local-variable 'minibuffer-default-add-function)
+				 (lambda ()
+				   (with-current-buffer
+				       (window-buffer (minibuffer-selected-window))
+				     (read-file-name-defaults dir initial)))))
                         (completing-read prompt 'read-file-name-internal
                                          pred mustmatch insdef
                                          'file-name-history default-filename)))
@@ -1997,6 +2040,17 @@
     (when newstr
       (completion-pcm-try-completion newstr table pred (length newstr)))))
 
+
+;; Miscellaneous
+
+(defun minibuffer-insert-file-name-at-point ()
+  "Get a file name at point in original buffer and insert it to minibuffer."
+  (interactive)
+  (let ((file-name-at-point
+	 (with-current-buffer (window-buffer (minibuffer-selected-window))
+	   (run-hook-with-args-until-success 'file-name-at-point-functions))))
+    (when file-name-at-point
+      (insert file-name-at-point))))
 
 (provide 'minibuffer)