In emacs, can I set up the *Messages* buffer so that it tails?

前端 未结 6 713
借酒劲吻你
借酒劲吻你 2021-01-07 22:42

Basically I want the *Messages* buffer to always scroll to the bottom when a new message arrives.

Can I do that?

相关标签:
6条回答
  • 2021-01-07 22:56

    For multiple frames you probably want:

    (defadvice message (after message-tail activate)
      "goto point max after a message"
      (with-current-buffer "*Messages*"
        (goto-char (point-max))
        (walk-windows (lambda (window)
                        (if (string-equal (buffer-name (window-buffer window)) "*Messages*")
                            (set-window-point window (point-max))))
                      nil
                      t)))
    
    0 讨论(0)
  • 2021-01-07 22:58

    Just put point at the end of the buffer M->. If you don't manually move it it will stay there -- IOW, you will always see the tail.

    0 讨论(0)
  • 2021-01-07 23:09

    Here's an implementation that uses the new advice style.

    (defun message-buffer-goto-end-of-buffer (&rest args)
      (let* ((win (get-buffer-window "*Messages*"))
             (buf (and win (window-buffer win))))
        (and win (not (equal (current-buffer) buf))
             (set-window-point
              win (with-current-buffer buf (point-max))))))
    
    (advice-add 'message :after 'message-buffer-goto-end-of-buffer)
    
    0 讨论(0)
  • 2021-01-07 23:09

    Here's an amendment over Peter's / Trey's solutions

    (defun modi/messages-auto-tail (&rest _)
      "Make *Messages* buffer auto-scroll to the end after each message."
      (let* ((buf-name "*Messages*")
             ;; Create *Messages* buffer if it does not exist
             (buf (get-buffer-create buf-name)))
        ;; Activate this advice only if the point is _not_ in the *Messages* buffer
        ;; to begin with. This condition is required; otherwise you will not be
        ;; able to use `isearch' and other stuff within the *Messages* buffer as
        ;; the point will keep moving to the end of buffer :P
        (when (not (string= buf-name (buffer-name)))
          ;; Go to the end of buffer in all *Messages* buffer windows that are
          ;; *live* (`get-buffer-window-list' returns a list of only live windows).
          (dolist (win (get-buffer-window-list buf-name nil :all-frames))
            (with-selected-window win
              (goto-char (point-max))))
          ;; Go to the end of the *Messages* buffer even if it is not in one of
          ;; the live windows.
          (with-current-buffer buf
            (goto-char (point-max))))))
    (advice-add 'message :after #'modi/messages-auto-tail)
    
    0 讨论(0)
  • 2021-01-07 23:10

    This code seems a bit overkill, but a the simple (goto-char (point-max)) wasn't working for me:

    (defadvice message (after message-tail activate)
      "goto point max after a message"
      (with-current-buffer "*Messages*"
        (goto-char (point-max))
        (let ((windows (get-buffer-window-list (current-buffer) nil t)))
          (while windows
            (set-window-point (car windows) (point-max))
            (setq windows (cdr windows))))))
    
    0 讨论(0)
  • 2021-01-07 23:15

    i run 23.3 and there were still way too many occasions where the built-in 'solution' and the orginal defadvice on the message function just didn't cut it, so i wrapped that code in a list / toggle / timer set up and it's working beautifully - no more frustration when debugging!

    it's generic, so works on any buffer, although i only really use it for..

    (toggle-buffer-tail "*Messages*" "on")
    

    ..hope it's useful to someone.

    ;alist of 'buffer-name / timer' items
    (defvar buffer-tail-alist nil)
    (defun buffer-tail (name)
      "follow buffer tails"
      (cond ((or (equal (buffer-name (current-buffer)) name)
             (string-match "^ \\*Minibuf.*?\\*$" (buffer-name (current-buffer)))))
            ((get-buffer name)
          (with-current-buffer (get-buffer name)
            (goto-char (point-max))
            (let ((windows (get-buffer-window-list (current-buffer) nil t)))
              (while windows (set-window-point (car windows) (point-max))
             (with-selected-window (car windows) (recenter -3)) (setq windows (cdr windows))))))))
    
    (defun toggle-buffer-tail (name &optional force)
      "toggle tailing of buffer NAME. when called non-interactively, a FORCE arg of 'on' or 'off' can be used to to ensure a given state for buffer NAME"
      (interactive (list (cond ((if name name) (read-from-minibuffer 
          (concat "buffer name to tail" 
            (if buffer-tail-alist (concat " (" (caar buffer-tail-alist) ")") "") ": ")
        (if buffer-tail-alist (caar buffer-tail-alist)) nil nil
               (mapcar '(lambda (x) (car x)) buffer-tail-alist)
            (if buffer-tail-alist (caar buffer-tail-alist)))) nil)))
      (let ((toggle (cond (force force) ((assoc name buffer-tail-alist) "off") (t "on")) ))
        (if (not (or (equal toggle "on") (equal toggle "off"))) 
          (error "invalid 'force' arg. required 'on'/'off'") 
          (progn 
            (while (assoc name buffer-tail-alist) 
               (cancel-timer (cdr (assoc name buffer-tail-alist)))
               (setq buffer-tail-alist (remove* name buffer-tail-alist :key 'car :test 'equal)))
            (if (equal toggle "on")
                (add-to-list 'buffer-tail-alist (cons name (run-at-time t 1 'buffer-tail name))))
            (message "toggled 'tail buffer' for '%s' %s" name toggle)))))
    

    edit: changed functionality to display tail at the bottom of the window

    0 讨论(0)
提交回复
热议问题