Skip to content

Instantly share code, notes, and snippets.

@haji-ali
Last active August 30, 2022 08:48
Show Gist options
  • Save haji-ali/0f3c2a17e3c4efc7ea4518088a7705b5 to your computer and use it in GitHub Desktop.
Save haji-ali/0f3c2a17e3c4efc7ea4518088a7705b5 to your computer and use it in GitHub Desktop.
Echo messages in minibuffer
;;; echo-line.el --- Echo messages in minibuffer -*- lexical-binding:t -*-
;;
;; Author: Al Haji-Ali <abdo.haji.ali@gmail.com>
;; Version: 0.1.0
;; Package-Requires: ((emacs "26.1"))
;; Keywords: message, echo, gui
;;
;; This file is not part of GNU Emacs.
;;
;; This program is free software; you can redistribute it and/or modify
;; it under the terms of the GNU General Public License as published by
;; the Free Software Foundation, either version 3 of the License, or
;; (at your option) any later version.
;;
;; This program is distributed in the hope that it will be useful,
;; but WITHOUT ANY WARRANTY; without even the implied warranty of
;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
;; GNU General Public License for more details.
;;
;; You should have received a copy of the GNU General Public License
;; along with this program. If not, see <http://www.gnu.org/licenses/>.
;;
;;; Commentary:
;; Based on gist by
;; https://gist.github.com/rougier/096323d35ae3af5c8d0740dbb297f3e5
;;
;; Example usage:
;;
;; (require 'echo-line)
;; ;; Enable `display-time-mode` to update the `display-time-string`
;; (display-time-mode 1)
;; ;; Optionally delete the time string from the mode-line
;; (delete 'display-time-string global-mode-string)
;; ;; Update the echo-line every time the display-time is updated
;; (add-hook 'display-time-hook 'echo-line-update)
;; ;; Set the echo-line format
;; (setq echo-line-format '((:eval display-time-string)))
;; (echo-line-mode)
(require 'subr-x)
;;; Code:
(defface echo-line-face
'((t :height 0.83
:overline t))
"Default face used for the echo line.")
(defcustom echo-line-format nil
"Template for displaying echo line in the minibuffer.
See `mode-line-format' for more info"
:type 'sexp)
(defvar echo-line--current nil
"The current string to display on the next update.")
(defcustom echo-line-text-scale-factor 0.83
"Scale factor to determine the size of character in the echo-line.
This is in relation to a character form the minibuffer.")
(defun echo-line-update ()
"Update the echo-line."
;; Make sure that we don't run in a TRAMP buffer
(let ((default-directory "~/"))
;; Prevent accidental modification of the current buffer
(with-temp-buffer
(setq echo-line--current
(format-mode-line echo-line-format))
;; Apply face while retaining set faces
(add-face-text-property 0 (length echo-line--current)
'echo-line-face t echo-line--current)))
(echo-line--re-echo))
(defun echo-line--process-message (raw-msg max-width)
"Process RAW-MSG to be displayed with the echo-line.
MAX-WIDTH is the maximum width for a message."
(let* ((msg (if-let ((index (text-property-any 0 (length raw-msg)
'echo-message-start t raw-msg)))
(substring raw-msg 0 index)
raw-msg))
(lines (if (string-match "\n\\([^\n]*\\)\\'" msg)
;; Extract last line
(cons (substring msg 0 (match-beginning 1))
(substring msg (match-beginning 1)))
(cons "" msg)))
(last-line (truncate-string-to-width
(cdr lines)
max-width
nil nil "…")))
(concat (car lines) last-line)))
(defun echo-line--message@advice (orig-fun &rest args)
"Message advice that add the echoed string to the message.
This advice displays the regular message in the echo area and
adds a specific text on the right part of the echo area.
Argument ORIG-FUN is `message`.
Optional argument ARGS are the functions arguments."
(if (or (string-empty-p echo-line--current) (active-minibuffer-window))
;; Regular log and display when minibuffer is active
(apply orig-fun args)
;; Enhanced display
(let* ((right-col-width (* echo-line-text-scale-factor
(string-width echo-line--current)))
(raw-msg (if (and args (car args))
(apply 'format-message args)
""))
(msg (echo-line--process-message
raw-msg
(- (frame-width) right-col-width)))
(full (concat msg
(propertize " "
'echo-message-start t
'display `((space
:align-to (- (+ right
right-fringe
right-margin)
,right-col-width))))
echo-line--current)))
;; Log actual message without echo
(when (and (not (string-empty-p raw-msg)) message-log-max)
(let ((inhibit-message t))
(funcall orig-fun "%s" raw-msg)))
;; Display enhanced message without log
(let ((message-truncate-lines t) (message-log-max nil))
(funcall orig-fun "%s" full))
;; Set current message explicitly.. Not really necessary.
(setq current-message raw-msg))))
(defun echo-line--re-echo ()
"Re-echo the current message to the echo-area."
(let ((message-log-max nil)
(msg (current-message)))
(if msg
(message "%s" msg)
(message nil))))
(define-minor-mode echo-line-mode
"Enables `echo-line-mode' globally."
:require 'echo-line
:init-value nil
:global t
(if (and echo-line-mode (display-graphic-p))
(progn
(advice-add 'message :around #'echo-line--message@advice)
(add-hook 'post-command-hook #'echo-line--re-echo)
(echo-line-update))
(advice-remove 'message #'echo-line--message@advice)
(remove-hook 'post-command-hook #'echo-line--re-echo)))
(provide 'echo-line)
;;; echo-line.el ends here
@haji-ali
Copy link
Author

@overideal, thanks for bringing echo-bar to my attention. I somehow didn't know about it!
If anything, it seems to be the superior option compared to the above snippet. I switched to it just now and will test it out over the coming days/weeks.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment