2

I have a function that creates a buffer, with a default file name, and would like the first user action for that buffer be a prompt to write the buffer to a file, allowing the user to change the default, but offering the default in the minibuffer prompt.

The best result of all the options I have tried so far does properly associate the desired filename and path with a buffer, and does prompt the user, but does not offer the default name as a default.

Here is one of the best versions of the test function:

(defun my-write-test ()
  (interactive)
  (let*
     ((name "foo.txt")
      (path "/home/me/foo.txt")
      (buf (create-file-buffer name)))
   (pop-to-buffer buf)
   (insert "random text\n")
   (set-visited-file-name path)
   (call-interactively 'write-file (vector path))))

My guess is that I am somehow not properly defining the vector argument to function call-interactively, but I could be wrong; Maybe I'm just using the wrong elisp function?

2
  • 2
    You may be interested in the function read-file-name, which can have default input, visible suggested initial input, etc. -- store the value to a let-bound variable and later pass that value to write-file. I'm not sure why you would need write-file interactively, if you use read-file-name. To see more about that function, type M-x describe-function or C-h f. And, unless I am mistaken, write-file does not take a vector -- it takes a STRING as its first argument. Again, describe the function write-file and see what the doc-string suggests for its arguments.
    – lawlist
    Commented Mar 18, 2018 at 21:54
  • @lawlist - YES! I was playing with that when you wrote the comment, and am close to posting an answer to my own question, that includes read-file-name. Commented Mar 18, 2018 at 22:12

3 Answers 3

1

I have the function now working to my satisfaction, as below, but am open to suggestions if I'm not doing things optimally. In the solution below, I use function read-file-name to prompt the user, and use condition-case to trap a user response of C-g, the quit signal, so that the default buffer file name is set for the buffer.

(defun my-write-test ()
  (interactive)
  (let*
     ((name "foo.txt")
      (dir "/home/me/")
      (buf (create-file-buffer name)))
   (pop-to-buffer buf)
   (insert "random text\n")
   (condition-case nil
     (progn ()
       (set-visited-file-name
         (expand-file-name
           (read-file-name "Save to: " dir nil nil name)))
       (save-buffer))
     (quit (set-visited-file-name name)))))
0

(This is more of a comment than an answer, but it required more space/formatting than a comment.)

(call-interactively 'write-file (vector path))

My guess is that I am somehow not properly defining the vector argument to function call-interactively

You seem to have mis-read the call-interactively documentation.

You can pass a vector to call-interactively, but only to pass a sequence of keys/events, and even then it's not the second argument, but the third:

(call-interactively FUNCTION &optional RECORD-FLAG KEYS)

Call FUNCTION, providing args according to its interactive calling specs. Return the value FUNCTION returns. The function contains a specification of how to do the argument reading. In the case of user-defined functions, this is specified by placing a call to the function ‘interactive’ at the top level of the function body. See ‘interactive’.

Optional second arg RECORD-FLAG non-nil means unconditionally put this command in the command-history. Otherwise, this is done only if an arg is read using the minibuffer.

Optional third arg KEYS, if given, specifies the sequence of events to supply, as a vector, if the command inquires which events were used to invoke it. If KEYS is omitted or nil, the return value of ‘this-command-keys-vector’ is used.

5
  • Thanks. I just now went back and tried another series of evaluations, but don't really understand how to use a vector of key events to pass a mandatory argument to a function that is evaluated using called-interactively, my example being function write-file. This kind of case (a function with a mandatory argument) doesn't seem like one that the emacs developers would have overlooked, so I'm still missing something fundamental. Commented Mar 19, 2018 at 0:52
  • 2
    Yes, the point is that you can't use the call-interactively arguments to pass arguments to the command being called. That's not what they are for (as is indicated by the docstring I've quoted, which explains what they are for). The whole point of call-interactively is that the command arguments are not passed directly, but instead obtained via the command's own interactive spec.
    – phils
    Commented Mar 19, 2018 at 1:49
  • 1
    I'm honestly a bit perplexed as to how/where you got the impression that you could do what you were attempting. I'm assuming it was just a guess on your part, in which case I highly recommend that you get into the habit of using C-h f to read the documentation of a function and learn what its arguments are for.
    – phils
    Commented Mar 19, 2018 at 1:54
  • What is a series of keys events?
    – Didier A.
    Commented Apr 6, 2020 at 5:35
  • @DidierA. A KEYS value would typically be a vector of integers and/or symbols. E.g. [3 118] (for the key sequence C-c v). See C-h i g (elisp) Input Events for details.
    – phils
    Commented Apr 6, 2020 at 8:20
0

Seems like this is all you're asking for.

(defun my-write-test ()
  (interactive)
  (let ((name  "foo.txt")
        (dir   "~/drews-lisp-20/bbbbb/"))
    (pop-to-buffer (find-file-noselect (expand-file-name name dir)))
    (insert "random text\n")
    (write-file (read-file-name "Save as file: " dir name))))

Did I miss something? The question is not too clear to me.

find-file-noselect is your friend.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.