2019-02-17 Inserting the current file name at point

It is not uncommon to issue some command to Emacs and find oneself in the minibuffer with the need to insert the name of the current file. A typical example would be firing M-! (shell-command) and realizing that we need to gice the name of the currently visited file as the argument to some command.

One way (admittedly, a roundabout one) to accomplish this is to fire Dired (if you use Dired-X, and I don’t see whay you shouldn’t, pressing C-x C-j (dired-jump) when visiting a file is a good way) and then press w (dired-copy-filename-as-kill). It comes in three variants: plain w, which just puts the filename at point into the kill ring, then C-u w, which does the same, but uses the relative path from the “main” directory of the Dired buffer (this makes a difference if you have a Dired buffer with subdirectories, which is one of the killer features of Dired as a file manager!). Lastly, you can say C-u 0 w (or M-0 w, or C-0 w), which requires a bit of finger acrobatics, but gives you the full path (which is sometimes exactly what you want).

That way, however, is very inconvenient. Imagine pressing M-!, typing half of the command line, and only then remembering that you need the filename. Then, you will probably quit (possibly killing the stuff typed so far), launch dired, use w, get back to M-! again, yank the filename and yank the rest of the command. Quite a mouthful. (Notice that you can go to another buffer without quitting the minibuffer, but this requires recursive minibuffers, and involves only a bit less keystrokes.)

So, imagine you could just press some key combo while in minibuffer (or anywhere else, for that matter) to get the name of the currently visited file inserted at point.

The main difficulty in coding this was how to get the name of the previous buffer when I’m in the minibuffer (which is an important use-case). With the help of the Emacs mailing list, I learned about minibuffer-selected-window, and getting a buffer when you have a window is easy.

(defun insert-current-file-name-at-point (&optional full-path)
  "Insert the current filename at point.
With prefix argument, use full path."
  (interactive "P")
  (let* ((buffer
	  (if (minibufferp)
	      (window-buffer
	       (minibuffer-selected-window))
	    (current-buffer)))
	 (filename (buffer-file-name buffer)))
    (if filename
	(insert (if full-path filename (file-name-nondirectory filename)))
      (error (format "Buffer %s is not visiting a file" (buffer-name buffer))))))

(global-set-key (kbd "M-o") #'insert-current-file-name-at-point)

Notice how I bind this to M-o, which is pretty useless in stock Emacs anyway. (Of course, feel free to bind to to whatever you like. Just be aware that you can use it with M-x, but if you want to use it in the minibuffer, you’ll need to set enable-recursive-minibuffers, which see, to a non-nil value.)

(Note: while writing this post, I noticed that user Alexander Klimov put an almost identical function in his message to the Emacs devel list. To make the situation even more embarassing, I actively participated in that thread, remembered the idea which inspired this post, and promptly forgot his code. Meh. I still decided to post it, if only because it is a useful function and I presume not everyone following my blog reads emacs-devel. Also, my code behaves slightly better in an edge case of a buffer which is not visiting any file.)

CategoryEnglish, CategoryBlog, CategoryEmacs