From 7d371eac644665cdc4afcac57e7a09560e010a31 Mon Sep 17 00:00:00 2001 From: Juri Linkov Date: Wed, 25 Nov 2009 17:11:29 +0000 Subject: [PATCH] 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'. --- etc/NEWS | 4 ++++ lisp/ChangeLog | 29 ++++++++++++++++++++++++++++ lisp/dired.el | 18 ++++++++++++------ lisp/ffap.el | 23 ++++++++++++++++++++++ lisp/files.el | 23 ++++++++++++---------- lisp/minibuffer.el | 56 +++++++++++++++++++++++++++++++++++++++++++++++++++++- 6 files changed, 136 insertions(+), 17 deletions(-) diff --git a/etc/NEWS b/etc/NEWS index 51c3ee62a60..58ff7c6705f 100644 --- a/etc/NEWS +++ b/etc/NEWS @@ -150,6 +150,10 @@ For instance, this can complete M-x lch to list-command-history. ** 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. diff --git a/lisp/ChangeLog b/lisp/ChangeLog index 3f392f8183e..5c7d0a1e25d 100644 --- a/lisp/ChangeLog +++ b/lisp/ChangeLog @@ -1,3 +1,32 @@ +2009-11-25 Juri Linkov + + 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 Really make the *Completions* window soft-dedicated (bug#5030). diff --git a/lisp/dired.el b/lisp/dired.el index 788c5be3289..17bb3449da9 100644 --- a/lisp/dired.el +++ b/lisp/dired.el @@ -599,12 +599,8 @@ Don't use that together with FILTER." (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 @@ Don't use that together with FILTER." ;; (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 @@ Keybindings: (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)) diff --git a/lisp/ffap.el b/lisp/ffap.el index a0e0d411206..7d3f279f724 100644 --- a/lisp/ffap.el +++ b/lisp/ffap.el @@ -1893,6 +1893,29 @@ Only intended for interactive use." (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 diff --git a/lisp/files.el b/lisp/files.el index 1bde2be0ef0..8478151cf78 100644 --- a/lisp/files.el +++ b/lisp/files.el @@ -411,6 +411,14 @@ and should return either a buffer or nil." :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 @@ its documentation for additional customization information." ;;(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 @@ Recursive uses of the minibuffer will not be affected." (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 @@ regardless of whether it was created literally or not. 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) diff --git a/lisp/minibuffer.el b/lisp/minibuffer.el index fdf57f9a2f8..cfe4d6f90de 100644 --- a/lisp/minibuffer.el +++ b/lisp/minibuffer.el @@ -1321,6 +1321,32 @@ such as making the current buffer visit no file in the case of (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 @@ and `read-file-name-function'." (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 @@ filter out additional entries (because TABLE migth not obey PRED)." (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) -- 2.11.4.GIT