Emacs Lisp: read-from-minibuffer Propels Deep Questions

By Xah Lee. Date: . Last updated: .

slightly frustrated with emacs lisp read-from-minibuffer. Spent now about a hour on this.

what i want is simple, like this:

(read-from-minibuffer
 (format "Directory (default %s):" default-directory) default-directory )

prompt user to enter a dir, with default of current dir.

however, according to inline doc of read-from-minibuffer, the second arg for default input is obsolete “mostly deprecated”. Instead, you have to use the 6th arg. Quote:

(read-from-minibuffer PROMPT &optional INITIAL-CONTENTS KEYMAP READ
HIST DEFAULT-VALUE INHERIT-INPUT-METHOD)

the doc is long so i won't paste here. See it by calling describe-function. Text from Minibuffer (ELISP Manual)

Now, so i do:

(read-from-minibuffer
 (format "Directory (default %s):" default-directory) nil nil nil nil default-directory)

doesn't work. Read the doc again, it turns out that the 4th arg must be true in order for the default value to work, else you get empty string if the user just press Enter.

So i do:

(read-from-minibuffer
 (format "Directory (default %s):" default-directory) nil nil t nil default-directory)

No go! because if the 4th arg is true, it means the input as a string will be fed to lisp reader, then interpreted as a lisp object. Hot damn. This means, if you want a string, you have to feed it "\"mystring\"". (the outter quote pair makes it a lisp string to be fed to lisp reader, then, the inner quote pair gets you a lisp string object)

So, now i have to do this:

(read-from-minibuffer
 (format "Directory (default %s):" default-directory) nil nil t nil (format "\"%s\"" default-directory) )

But no! Because, now if user actually enter a value, for example, type mary, lisp reader freaks out. Again, it doesn't undertand what the letter sequence mary is. It wants a string "\"mary\"". So, user will have to actually type "mary" for this to work.

WTF?

This line is supposed to be done in 20 seconds. I think i've spent 40 min on this. Now, my mind wanders to the deep question of humanity….

Xah

Solution

You should use read-file-name or read-directory-name. Like this:

(defun test-fun (arg)
  "Prompt user to enter a file path, with file name completion and input history support."
  (interactive (list (read-file-name "Open directory:")) )
  (message "Path is 「%s」." arg) )

If you really want to use read-from-minibuffer, you can simply:

(read-from-minibuffer "Directory: " default-directory)

Or, check if the return string is empty.

(let ((directory (read-from-minibuffer
                  (format "Directory (default %s): " default-directory)
                  nil nil nil nil default-directory)))
  (if (equal directory "")
      default-directory
    directory))

Thanks to Kevin Rodgers, Drew Adams http://emacswiki.org/emacs/DrewAdams, [Elias Mårtenson https://plus.google.com/115209488640908180409/posts]. Source groups.google.com