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?

user1404316
  • 769
  • 5
  • 12
  • 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 Mar 18 '18 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`. – user1404316 Mar 18 '18 at 22:12

3 Answers3

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)))))
Drew
  • 75,699
  • 9
  • 109
  • 225
user1404316
  • 769
  • 5
  • 12
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.

phils
  • 48,657
  • 3
  • 76
  • 115
  • 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. – user1404316 Mar 19 '18 at 00: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 Mar 19 '18 at 01: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 Mar 19 '18 at 01:54
  • What is a series of keys events? – Didier A. Apr 06 '20 at 05: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 Apr 06 '20 at 08: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.

Drew
  • 75,699
  • 9
  • 109
  • 225