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?

ReneFroger
  • 3,855
  • 22
  • 63
  • Do you mean permanently displayed or just when you request it? – Nathanael Farley Feb 03 '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 03 '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 03 '16 at 16:46
  • Related: http://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 03 '16 at 17:28
  • @Nathanael: permanently displayed. – ReneFroger Feb 03 '16 at 18:16
  • @Kaushal Modi: interesting. Thanks for the link. Will figure out how I could update it only when a buffer gets killed or added. And changes it when it switches from buffer. – ReneFroger Feb 03 '16 at 18:17

1 Answers1

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)))
Tobias
  • 32,569
  • 1
  • 34
  • 75
  • 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 06 '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 07 '16 at 21:41
  • Related: http://stackoverflow.com/questions/28830990/how-to-empty-or-clear-the-emacs-minibuffer. – Tobias Feb 08 '16 at 14:06
  • Also related: http://emacs.stackexchange.com/questions/5571/display-list-of-words-along-bottom-of-frame – Tobias Feb 09 '16 at 06: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 09 '16 at 21:13