1

When I was using Vim in the past, I was charmed of the vim-bufferline. Only when there were not any message displayed or any command invoked, it showed the opened buffers in the minibuffer. It looked like this:

enter image description here

5 buffers opened, the file command is being displayed. It also worked well with multiple buffers in multiple windows opened.

By default my minibuffer is empty, when there is no message. I would like to display the current buffers in that minibuffer. When googling around, I found no Emacs equivalence of this.

Any suggestions?

  • Do you mean permanently displayed or just when you request it? – Nathanael Farley Feb 3 '16 at 16:41
  • 2
    (I ask, because Emacs opens a load of buffers, so it would get very crowded, very quickly) – Nathanael Farley Feb 3 '16 at 16:41
  • I know of no library or predefined mode that gives you this. But it shouldn't be hard to write. Just update mode-line-format when a buffer is created or deleted. But if you just want the buffer names on demand, then just use C-x b TAB (then C-g, if you don't want to switch buffers). – Drew Feb 3 '16 at 16:46
  • Related: emacs.stackexchange.com/a/19856/115 Try updating the minibuffer-line-format variable to show you the buffer list. You will need to install the minibuffer-line package from GNU ELPA. – Kaushal Modi Feb 3 '16 at 17:28
  • @Nathanael: permanently displayed. – ReneFroger Feb 3 '16 at 18:16
2

I had already some lines of code when KaushalModi mentioned minibuffer-line. Maybe, you should use that mode. If you like you can steal some ideas from the following code to get the updates on buffer-changing operations. It works. But, it is not very efficient.

For an example you could use buffer-list-update-hook instead of post-command-hook.

You can also plug buffer-file-name into minibuffer-buffer-list-filter instead of identity. This way emacs only shows file buffers.

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; Show buffer list in minibuffer

(defcustom minibuffer-buffer-list-filter 'identity
  "Function for filtering the buffer names shown in the minibuffer window."
  :group 'notify
  :type 'sexp)

(defun substring-no-properties-safe (str from &optional to)
  "Like `substring-no-properties' but does not fail on args out of range."
  (let ((n (length str)))
    (substring-no-properties str (min from (max (1- n) 0)) (when to (min to n)))))

(defun minibuffer-buffer-list ()
  "Show buffer list in minibuffer window."
  (substring-no-properties-safe
    (mapconcat 'buffer-name
           (cl-delete-if-not minibuffer-buffer-list-filter
                 (cl-delete-if (lambda (buf) (eq (aref (buffer-name buf) 0) #x20))
                           (buffer-list)))
           " | ")
    0 (1- (window-width (minibuffer-window)))))

(defcustom notify-function 'minibuffer-buffer-list
  "The string returned by this function is displayed in minibuffer."
  :group 'notify
  :type 'sexp)

(defcustom notify-idle-time 1
  "Time in seconds until showing buffers in minibuffer."
  :group 'notify
  :type 'number)

(defvar notify-idle-timer nil)

(defun notify-text-p ()
  "Check whether text in current buffer has the :notification text property."
  (get-text-property (point-min) :notification))

(defmacro notify-with-minibuffer (&rest body)
  "Run BODY in minibuffer when there is one."
  (declare '(debug &rest body) '(indent 1))
  `(let ((minibuffer (window-buffer (minibuffer-window))))
     (when minibuffer
       (with-current-buffer minibuffer
       ,@body))))

(defun notify-clear ()
  "Clear notification from active minibuffer."
  (notify-with-minibuffer
   (when (notify-text-p)
     (with-silent-modifications
       (erase-buffer)))))

(defun notify-run (&optional frame)
  "Run `notify-function' if minibuffer is empty and emacs is idle for `notify-idle-time' seconds."
  (notify-with-minibuffer
   (when (and (null (active-minibuffer-window))
          (or (eq (length (buffer-string)) 0)
          (notify-text-p)))
     (with-silent-modifications
       (erase-buffer)
       (insert (propertize
        (funcall notify-function)
        :notification t)))
     )))

(define-minor-mode notify-mode
  "Show whatever `notify-function' wants us to show after `notify-idle-time' in minibuffer."
  nil nil nil
  (if notify-mode
      (progn
    (setq notify-idle-timer (run-with-idle-timer notify-idle-time t 'notify-run))
    (add-hook 'post-command-hook 'notify-run)
    (add-hook 'window-size-change-functions 'notify-run))
    (if (timerp notify-idle-timer)
    (cancel-timer notify-idle-timer))
    (notify-clear)
    (remove-hook 'post-command-hook 'notify-run)
    (remove-hook 'window-size-change-functions 'notify-run))
  :global 't)

The documentation on the echo area motivates the following implementation. Especially the functions notify-p, notify-run and notify-clear have changed.

(require 'cl-lib)

(defcustom minibuffer-buffer-list-filter 'identity
  "Function for filtering the buffer names shown in the minibuffer window."
  :group 'notify
  :type 'sexp)

(defun substring-no-properties-safe (str from &optional to)
  "Like `substring-no-properties' but does not fail on args out of range."
  (let ((n (length str)))
    (substring-no-properties str (min from (max (1- n) 0)) (when to (min to n)))))

(defun notify-buffer-list ()
  "Show buffer list in minibuffer window."
  (substring-no-properties-safe
    (mapconcat 'buffer-name
           (cl-delete-if-not minibuffer-buffer-list-filter
                 (cl-delete-if (lambda (buf) (eq (aref (buffer-name buf) 0) #x20))
                           (buffer-list)))
           " | ")
    0 (1- (window-width (minibuffer-window)))))

(defcustom notify-function 'notify-buffer-list
  "The string returned by this function is displayed in minibuffer."
  :group 'notify
  :type 'sexp)

(defcustom notify-idle-time 1
  "Time in seconds until showing buffers in minibuffer."
  :group 'notify
  :type 'number)

(defvar notify-idle-timer nil)

(defun notify-p ()
  "Check whether the message buffer is ready for updating the notification.
That means either it is empty or contains already an old notification."
  (when (null (active-minibuffer-window))
    (let ((msg (current-message)))
      (or (null msg)
      (get-text-property 0 :notification msg)))))

(defun notify-clear ()
  "Clear notification from active minibuffer."
  (when (notify-p)
    (message nil)))

(defun notify-run (&optional frame)
  "Run `notify-function' if minibuffer is empty and emacs is idle for `notify-idle-time' seconds."
  (when (notify-p)
    (let (message-log-max) ;; inhibit copy to message buffer
      (message "%s" (propertize
             (funcall notify-function)
             :notification t)))))

(define-minor-mode notify-mode
  "Show whatever `notify-function' wants us to show after `notify-idle-time' in minibuffer."
  nil nil nil
  :global t
  (if notify-mode
      (progn
    (setq notify-idle-timer (run-with-idle-timer notify-idle-time t 'notify-run))
    (add-hook 'post-command-hook 'notify-run)
    (add-hook 'window-size-change-functions 'notify-run))
    (if (timerp notify-idle-timer)
    (cancel-timer notify-idle-timer))
    (remove-hook 'window-size-change-functions 'notify-run)
    (remove-hook 'post-command-hook 'notify-run)
    (notify-clear)))
  • Thanks for your reply. I installed minibuffer-line, and in your code I changed buffer-file-name into minibuffer-buffer-list-filter instead of identity, and evaluated your code. But I see not any difference. Are you using this code by the way in your dot-Emacs config? Because it's a lot of code to be written for an answer. :-O – ReneFroger Feb 6 '16 at 14:25
  • The code is independent minibuffer-line. It defines a new global minor mode notify-mode which you can switch on with M-x notify-mode. Afterwards the buffers are shown in the minibuffer when it is available. I have to admit that I still have difficulties to switch notify-mode off reliably. It seems that the string in the message buffer is cached and restored. notify-mode cleans up all of its hooks and timers but still the old message reappears when the mode is switched off. In message.el they do (message nil) to clear the minibuffer. But this does not help either. Hm... – Tobias Feb 7 '16 at 21:41
  • Related: stackoverflow.com/questions/28830990/…. – Tobias Feb 8 '16 at 14:06
  • Also related: emacs.stackexchange.com/questions/5571/… – Tobias Feb 9 '16 at 6:37
  • please forgive my belated response. Thanks for your replies. After switching notify-mode, I get it working with my buffer names (how could I not notice that in the source?) So thanks! But as far as I can see, it lists the buffer which is currently viewed as the first one, and changes every time when I switch from bufffer. I would it make it static, so I will invent some Elisp to manage that, with the help of the links you have provided. You have already laid a good solid source for that. Thanks!! – ReneFroger Feb 9 '16 at 21:13

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

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