1

I've been trying to create a command that, when run, calls another function (my-function) which reads from the minibuffer and does some processing on the input. I want the command history for this command (my-command) to be buffer-local, so that each buffer will have a separate input history.

The input history is supposed to be stored in variable my-hist. Here's a summarized version of my code:

;; -*- lexical-binding: t; -*-

(defun my-function (hist)
  (read-from-minibuffer "> " nil nil nil 'hist))

(defvar my-hist nil)
(make-variable-buffer-local 'my-hist)

(defun my-command ()
  (interactive)
  (add-to-history 'my-hist (my-function 'my-hist)))

I've found that after executing my-command from two different buffers, their input history is shared, for some reason. Using M-p and M-n I can go back and forwards through all the inputs I've entered from any of the two buffers. However, if I inspect my-hist using M-:, the input history appears to be correct for each buffer.

Could this be related to how read-from-minibuffer looks up the value pointed at by symbol 'hist?

3 Answers 3

1

What I'm actually trying to do is ... calling read-number from a separate function (my-command) with a buffer-local variable as HIST.

As read-number doesn't have its own history list, you could let-bind minibuffer-history to your buffer-local history list around your call to read-number.

(defvar-local local-history nil
  "Buffer-local history.")

(defun my-command ()
  "Read a number."
  (interactive)
  (message "Number was %f"
           (let ((minibuffer-history local-history))
             (prog1 (read-number "Num: ")
               (setq-local local-history minibuffer-history)))))

That setq-local is needed because, following the read-number call, local-history might only be the cdr of the minibuffer-history list (i.e. if a new value was pushed to the history), so it needs to be updated to the current value.

1
  • This does the trick and answers my original question, thanks. Additionally, we may have found a problem with read-from-minibuffer when a buffer-local variable is used for hist.
    – Federico
    Commented Nov 21, 2019 at 21:14
2

Do this:

(defvar my-hist nil)
(make-variable-buffer-local 'my-hist)

(defun my-function (hist)
  (read-from-minibuffer "> " nil nil nil hist))

(defun my-command ()
  (interactive)
  (add-to-history 'my-hist (my-function 'my-hist)))

Don't quote hist when you pass it to read-from-minibuffer. You want to pass its value, e.g. the symbol my-hist, and not the result of evaluating 'hist, which is the symbol hist.

But I agree with you about using M-p etc. Function read-from-minibuffer apparently doesn't use the buffer-local value of the history variable - either for access or for updating (adding to it).

That would also explain why, to add to the buffer-local value you need to explicitly use add-to-history, passing the value of the input that was read.

Normally (i.e., if the history var is not buffer-local), you would just invoke (my-function 'my-hist) in my-command - read-from-minibuffer automatically adds the read input to the history variable. But doing that when the var is buffer-local doesn't update it.

Seems like a bug. But this is longstanding behavior (I see it back to Emacs 22, and I see the fact that it doesn't work without add-to-history even back to Emacs 20, which doesn't have add-to-history). So I'm probably missing something. Hopefully someone else will enlighten us.

(completing-read behaves the same way.)

Good question.

3
  • Thanks for the answer. Thanks for the tip regarding 'hist and hist as well, that had me confused when I initially wrote it.
    – Federico
    Commented Nov 20, 2019 at 22:52
  • 2
    Thanks for filing the bug: #38282
    – Drew
    Commented Nov 21, 2019 at 21:07
  • That was actually the wishlist item I filed before asking here about my problem, I'll file a separate one for read-from-minibuffer separately!
    – Federico
    Commented Nov 21, 2019 at 21:13
2

Here's how to do it buffer-locally:

(defvar-local my-hist-symbol nil)

(defun my-function ()
  (unless my-hist-symbol
    ;; Create the buffer's history symbol.
    (setq-local my-hist-symbol (make-symbol "my-hist")))
  (read-from-minibuffer "> " nil nil nil my-hist-symbol))

(defun my-command ()
  (interactive)
  (my-function))

(defun my-show-buffer-history ()
  (interactive)
  (if (null my-hist-symbol)
      (message "No history yet")
    (message "%s" (symbol-value my-hist-symbol))))
10
  • 1
    Your comment should really be a separate question for emacs.SE, I think. It may be the "real" question you had, but it is a different question.
    – Drew
    Commented Nov 21, 2019 at 0:04
  • 1
    @stefan: Is the need to do things that way because read-from-minibuffer doesn't consider the buffer which is current when you invoke my-command to be the current buffer, for purposes of getting the value of the variable (and so it uses the global default value)? It's not clear to me why read-from-minibuffer doesn't already DTRT (in terms of expectation), using the local value of the var in that buffer.
    – Drew
    Commented Nov 21, 2019 at 0:08
  • 1
    @Drew: AFAICT, the reason why you need this is because read-from-minibuffer (or more specifically read_minibuf in minibuf.c) modifies the history var before exiting the minibuffer so it affects the value local to the minibuffer. I can't think of a good reason for it, so it might qualify as a bug.
    – Stefan
    Commented Nov 21, 2019 at 13:36
  • 1
    @Stefan: OK, thanks. You'd probably be the best-placed to file such a bug. Seems like, at the very least, users should not have to jump through hoops to be able to use buffer-local history variables. Or else maybe something about the impossibility/difficulty should be documented.
    – Drew
    Commented Nov 21, 2019 at 15:10
  • 1
    @Drew: I'm not affected by this bug, so no I'm not best placed to report it
    – Stefan
    Commented Nov 21, 2019 at 16:11

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.