;;; awesome-tray.el --- Modular tray bar ;; Filename: awesome-tray.el ;; Description: Modular tray bar ;; Author: Andy Stewart ;; Maintainer: Andy Stewart ;; Copyright (C) 2018, Andy Stewart, all rights reserved. ;; Created: 2018-10-07 07:30:16 ;; Version: 4.2 ;; Last-Updated: 2022-03-01 11:02:39 ;; By: Andy Stewart ;; URL: http://www.emacswiki.org/emacs/download/awesome-tray.el ;; Keywords: ;; Compatibility: GNU Emacs 28.1 ;; ;; Features that might be required by this library: ;; ;; `cl-lib' ;; `subr-x' ;; `battery' ;; ;;; This file is NOT part of GNU Emacs ;;; License ;; ;; 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, 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; see the file COPYING. If not, write to ;; the Free Software Foundation, Inc., 51 Franklin Street, Fifth ;; Floor, Boston, MA 02110-1301, USA. ;;; Commentary: ;; ;; Modular tray bar. ;; ;; I don't like mode-line, it's too high, affect me to read the code. ;; With Emacs, we only need to focus on very little information, such as time, current mode, git branch. ;; Excessive information can seriously interfere with our attention. ;; ;;; Installation: ;; ;; Put awesome-tray.el to your load-path. ;; The load-path is usually ~/elisp/. ;; It's set in your ~/.emacs like this: ;; (add-to-list 'load-path (expand-file-name "~/elisp")) ;; ;; And the following to your ~/.emacs startup file. ;; ;; (require 'awesome-tray) ;; (awesome-tray-mode 1) ;; ;; No need more. ;;; Customize: ;; ;; `awesome-tray-mode-line-active-color' ;; `awesome-tray-mode-line-inactive-color' ;; `awesome-tray-active-modules' ;; `awesome-tray-git-update-duration' ;; `awesome-tray-refresh-idle-delay' ;; `awesome-tray-buffer-name-buffer-changed' ;; `awesome-tray-buffer-name-buffer-changed-style' ;; `awesome-tray-input-method-default-style' ;; `awesome-tray-input-method-local-style' ;; `awesome-tray-input-method-local-methods' ;; `awesome-tray-buffer-read-only-style' ;; ;; All of the above can customize by: ;; M-x customize-group RET awesome-tray RET ;; ;;; Change log: ;; ;; 2023/07/06 ;; * Make mode-line color indicate buffer state configurable by `awesome-tray-adjust-mode-line-color-enable'. ;; ;; 2023/07/01 ;; * Make mode-line color indicate buffer state. ;; ;; 2023/06/30 ;; * `awesome-tray-module-location-or-page-info' support EAF PDF Viewer ;; ;; 2023/06/12 ;; * Add `awesome-tray-module-location-or-page-info' to show location or pdf page. ;; ;; 2023/06/03 ;; * Add `awesome-tray-module-celestial-info' to show moon phase date and sunrise/sunset time. ;; * Add `awesome-tray-location-info-all', `awesome-tray-location-info-top', ;; and `awesome-tray-location-info-bottom' to use custom string for All, Top and Bottom in buffer location info. ;; ;; 2022/03/01 ;; * Use overlay re-implement tray information render. ;; ;; 2020/06/18 ;; * Shorter date info. ;; ;; 2020/05/06 ;; * Just show origin message if got any error, easy to debug. ;; ;; 2020/04/01 ;; * Shorter tray info. ;; ;; 2020/02/27 ;; * Adapter the latest version of the snails. ;; * Adjust algorithm of `awesome-tray-get-frame-width'. ;; ;; 2020/02/19 ;; * Add week info in date. ;; ;; 2020/02/14 ;; * Add `awesome-tray-battery-update-duration' to fix `set-mark-command' failed. ;; ;; 2020/02/10 ;; * Add battery remaining time. ;; ;; 2020/02/05 ;; * Add battery status. ;; ;; 2020/01/05 ;; * Hide awesome-tab info if it is too long. ;; ;; 2019/08/20 ;; * Use variable `awesome-tray-mode-line-default-height' fix issue #34. ;; ;; 2019/08/14 ;; * Remove notify message when toggle awesome-tray status. ;; ;; 2019/08/13 ;; * Keep tray info align right when message is very long, thanks QiangF. ;; ;; 2019/07/26 ;; * Support snails framework. ;; ;; 2019/07/16 ;; * Use `format-mode-line' improve performance of `awesome-tray-module-location-info'. ;; ;; 2019/07/15 ;; * Use current-line save value of `line-number-at-pos', improve the performance of `awesome-tray-module-location-info'. ;; * Use `ignore-errors' catch error of awesome-tray. ;; ;; 2019/07/14 ;; * Don't wrap awesome-tray info if variable `inhibit-message' is non-nil. ;; ;; 2019/06/23 ;; * Support `awesome-tab' group indicator. ;; * Fix crash cause by `awesome-tray-module-awesome-tab-info' ;; ;; 2019/05/08 ;; * Disable git modulde default, it have performance when we change buffer too fast. ;; ;; 2019/04/29 ;; * Fix position not update when execute command `beginning-of-buffer' or `end-of-buffer'. ;; ;; 2019/04/25 ;; * Add 'circe' module displaying circe tracking-buffer modeline info. ;; * The circe module is not activated by default, it's added to `awesome-tray-all-modules'. ;; ;; 2018/11/25 ;; * Add `RVM' support. ;; * The rvm module is not activated by default, I move it to `awesome-tray-all-modules'. ;; ;; 2018/11/18 ;; * Fix the problem of displaying duplicate information when the mouse is in the minibuffer window. ;; ;; 2018/11/12 ;; * Remove Mac color, use hex color instead. ;; ;; 2018/11/03 ;; * Add percent information in location module. ;; * Fix error: Not enough arguments for format string. ;; ;; 2018/10/29 ;; * Use `unspecified' attribute fix black block of mode-line inactive status. ;; * Add `awesome-tray-git-update-duration' option. ;; ;; 2018/10/21 ;; * Use `advice-add' re-implmenet `awesome-tray-message-advice' ;; * Add parent-dir module. ;; * Don't show parent-dir if current mode is `dired-mode'. ;; ;; 2018/10/13 ;; * Use `awesome-tray-process-exit-code-and-output' fetch git current branch for better error handling. ;; ;; 2018/10/11 ;; * Reimplement `awesome-tray-module-git-info' don't depend on magit. ;; * Add last-command module, handy for debug emacs. ;; ;; 2018/10/09 ;; * Add new option `awesome-tray-active-modules'. ;; ;; 2018/10/07 ;; * First released. ;; * Add row/column information. ;; * Add `awesome-tray-message-advice' make tray information visible always. ;; * Use `frame-width' instead `window-width' to handle blank characters fill. ;; * Don't fill blank if message string is wider than frame width. ;; ;;; Acknowledgements: ;; ;; ;; ;;; TODO ;; ;; ;; ;;; Require (require 'cl-lib) (require 'subr-x) (require 'battery) (require 'timer) (require 'minibuffer) (require 'overlay) (require 'vc-git) (require 'format-spec) (require 'awesome-tray-faces) ;;; Code: (defgroup awesome-tray nil "Modular tray bar." :group 'awesome-tray) (defcustom awesome-tray-minibuffer t "If non-nil, also display the awesome-tray when in the minibuffer." :group 'awesome-tray :type 'boolean) (defcustom awesome-tray-second-line nil "If non-nil, display awesome-tray in a second line. WARNING! This makes a lot of minibuffer interactions look weird, disable it if you have any problems with your minibuffer appearence." :group 'awesome-tray :type 'boolean) (defcustom awesome-tray-hide-mode-line t "If non-nil, make the mode-line very thin and highlight it when its active/inactive." :group 'awesome-tray :type 'boolean) (defcustom awesome-tray-position 'right "Position to display awesome-tray. WARNING! Better to be used with `awesome-tray-second-line' enabled, This makes a lot of minibuffer interactions look weird, disable it if you have any problems with your minibuffer appearence." :group 'awesome-tray :type 'symbol) (defcustom awesome-tray-git-show-status t "If non-nil, display the current file status in the git module." :group 'awesome-tray :type 'boolean) (defcustom awesome-tray-evil-show-mode t "If non-nil, display the current evil mode in the evil module." :group 'awesome-tray :type 'boolean) (defcustom awesome-tray-evil-show-macro t "If non-nil, display the current recording macro in the evil module." :group 'awesome-tray :type 'boolean) (defcustom awesome-tray-evil-show-cursor-count t "If non-nil, display the current multiple cursors count in the evil module." :group 'awesome-tray :type 'boolean) (defcustom awesome-tray-meow-show-mode t "If non-nil, display the current meow mode in the meow module." :group 'awesome-tray :type 'boolean) (defcustom awesome-tray-update-interval 1 "Interval in seconds between updating the awesome-tray contents. If nil, don't update the awesome-tray automatically." :group 'awesome-tray :type 'number) (defcustom awesome-tray-mode-line-active-color "DarkRed" "Active color." :type 'string :group 'awesome-tray) (defcustom awesome-tray-mode-line-inactive-color "Gray10" "Inactive color." :type 'string :group 'awesome-tray) (defcustom awesome-tray-mode-line-modified-readonly-color "Green" "Modified + readonly color." :type 'string :group 'awesome-tray) (defcustom awesome-tray-mode-line-readonly-color "DarkGreen" "Readonly color." :type 'string :group 'awesome-tray) (defcustom awesome-tray-mode-line-modified-color "DarkOrange" "Modified color." :type 'string :group 'awesome-tray) (defcustom awesome-tray-adjust-mode-line-color-enable nil "If non-nil, adjust mode-line color when buffer state changes." :group 'awesome-tray :type 'boolean) (defcustom awesome-tray-mode-line-height 0.1 "Height of mode line." :type 'integer :group 'awesome-tray) (defcustom awesome-tray-active-modules '("location" "belong" "file-path" "mode-name" "battery" "date") "Default active modules." :type 'list :group 'awesome-tray) (defcustom awesome-tray-essential-modules '("location" "belong" "file-path") "Default essential modules, show when minibuffer is too long." :type 'list :group 'awesome-tray) (defcustom awesome-tray-git-update-hooks '(after-save-hook after-revert-hook vc-checkin-hook text-scale-mode-hook) "Hook points to update the git module." :type '(list (hook :tag "HookPoint") (repeat :inline t (hook :tag "HookPoint")))) (defcustom awesome-tray-separator " " "Default string for the separator between modules." :group 'awesome-tray :type 'string) (defcustom awesome-tray-location-info-all "" "Default string indicating buffer all." :group 'awesome-tray :type 'string) (defcustom awesome-tray-location-info-top " T" "Default string indicating buffer top." :group 'awesome-tray :type 'string) (defcustom awesome-tray-location-info-bottom " B" "Default string indicating buffer bottom." :group 'awesome-tray :type 'string) (defcustom awesome-tray-ellipsis "…" "Default string for the ellipsis when something is truncated." :group 'awesome-tray :type 'string) (defcustom awesome-tray-date-format "%-m-%-d %-H:%-M %a" "Format string of the date module." :group 'awesome-tray :type 'string) (defcustom awesome-tray-mpd-format "%a - %t" "Format string of the mpd module. %t title %a artist %A album %p position on the playlist %P playlist-length %f filename without the folder and file extension %F regular filename" :group 'awesome-tray :type 'string) (defcustom awesome-tray-git-format "git:%s" "Format string of the git module. %s branch and file status if enabled with `awesome-tray-git-show-status'" :group 'awesome-tray :type 'string) (defcustom awesome-tray-location-format "%l:%c %p" "Format string of the location module. See `mode-line-format'" :group 'awesome-tray :type 'string) (defcustom awesome-tray-buffer-name-max-length 20 "Max length of buffer name." :group 'awesome-tray :type 'int) (defcustom awesome-tray-mpd-title-max-length 20 "Max length of mpd song title and filename." :group 'awesome-tray :type 'int) (defcustom awesome-tray-file-name-max-length 20 "Max length of file name." :group 'awesome-tray :type 'int) (defcustom awesome-tray-volume-update-duration 5 "Update duration of volume status, in seconds." :type 'integer :group 'awesome-tray) (defcustom awesome-tray-github-update-duration 120 "Update duration of github notification, in seconds." :type 'integer :group 'awesome-tray) (defcustom awesome-tray-github-erase-duration 30 "Github notification time before it gets removed from the bar, in seconds." :type 'integer :group 'awesome-tray) (defcustom awesome-tray-belong-update-duration 5 "Update duration of which class, in seconds." :type 'integer :group 'awesome-tray) (defcustom awesome-tray-battery-update-duration 5 "Update duration of battery status, in seconds. It will make command `set-mark-command' failed if not use duration." :type 'integer :group 'awesome-tray) (defcustom awesome-tray-refresh-idle-delay 0.5 "Update idle delay of awesome tray, in seconds." :type 'double :group 'awesome-tray) (defcustom awesome-tray-buffer-name-buffer-changed-style "*" "`awesome-tray-buffer-name-buffer-changed' style." :type 'string :group 'awesome-tray) (defcustom awesome-tray-buffer-name-buffer-changed nil "Show the current buffer changes after buffer-name." :type 'boolean :group 'awesome-tray) (defcustom awesome-tray-input-method-default-style "" "Default input method display style for input-method module." :type 'string :group 'awesome-tray) (defcustom awesome-tray-input-method-local-style "ZH" "Local input method display style for input-method module." :type 'string :group 'awesome-tray) (defcustom awesome-tray-input-method-local-methods '("rime") "List of local input methods you want to display `awesome-tray-input-method-local-style'" :type 'list :group 'awesome-tray) (defcustom awesome-tray-buffer-read-only-style "R-O" "Display style for buffer-read-only module." :type 'string :group 'awesome-tray) (defcustom awesome-tray-file-path-show-filename nil "Show filename in file-path module or not." :type 'boolean :group 'awesome-tray) (defcustom awesome-tray-file-path-truncated-name-length 1 "In file-path module, how many letters to leave when truncate dirname. Beginning dots are not counted." :type 'integer :group 'awesome-tray) (defcustom awesome-tray-file-path-full-dirname-levels 2 "In file-path module, how many levels of parent directories should be shown in their full name." :type 'integer :group 'awesome-tray) (defcustom awesome-tray-info-padding-right 0 "You can customize right padding to avoid awesome-tray wrap sometimes." :type 'integer :group 'awesome-tray) (defvar awesome-tray-text nil "The text currently displayed in the awesome-tray.") (defvar awesome-tray-overlays nil "List of overlays displaying the awesome-tray contents.") (defvar awesome-tray-before-github-fetch-notification-hook nil "Hooks before fetching GitHub notifications. Example: (add-hook \\='awesome-tray-before-github-fetch-notification-hook #\\='auth-source-pass-enable)") (defvar awesome-tray--github-notification-number 0) (defvar awesome-tray-github-last-time 0) (defvar awesome-tray-mode-line-colors nil) (defvar awesome-tray-mpd-command-cache "") (defvar awesome-tray-git-command-cache "") (defvar awesome-tray-git-buffer-filename "") (defvar awesome-tray-belong-last-time 0) (defvar awesome-tray-belong-last-buffer nil) (defvar awesome-tray-belong-cache "") (defvar awesome-tray-battery-status-last-time 0) (defvar awesome-tray-battery-status-cache "") (defvar awesome-tray-flycheck-status-cache "") (defvar awesome-tray-last-tray-info nil) (defvar awesome-tray-mode-line-default-height 1) (defvar awesome-tray-module-alist '(("awesome-tab" . (awesome-tray-module-awesome-tab-info awesome-tray-module-awesome-tab-face)) ("buffer-name" . (awesome-tray-module-buffer-name-info awesome-tray-module-buffer-name-face)) ("circe" . (awesome-tray-module-circe-info awesome-tray-module-circe-face)) ("date" . (awesome-tray-module-date-info awesome-tray-module-date-face)) ("celestial" . (awesome-tray-module-celestial-info awesome-tray-module-celestial-face)) ("date" . (awesome-tray-module-date-info awesome-tray-module-date-face)) ("evil" . (awesome-tray-module-evil-info awesome-tray-module-evil-face)) ("file-path" . (awesome-tray-module-file-path-info awesome-tray-module-file-path-face)) ("git" . (awesome-tray-module-git-info awesome-tray-module-git-face)) ("last-command" . (awesome-tray-module-last-command-info awesome-tray-module-last-command-face)) ("location" . (awesome-tray-module-location-info awesome-tray-module-location-face)) ("location-or-page" . (awesome-tray-module-location-or-page-info awesome-tray-module-location-or-page-face)) ("parent-dir" . (awesome-tray-module-parent-dir-info awesome-tray-module-parent-dir-face)) ("mode-name" . (awesome-tray-module-mode-name-info awesome-tray-module-mode-name-face)) ("rvm" . (awesome-tray-module-rvm-info awesome-tray-module-rvm-face)) ("battery" . (awesome-tray-module-battery-info awesome-tray-module-battery-face)) ("input-method" . (awesome-tray-module-input-method-info awesome-tray-module-input-method-face)) ("buffer-read-only" . (awesome-tray-module-buffer-read-only-info awesome-tray-module-buffer-read-only-face)) ("belong" . (awesome-tray-module-belong-info awesome-tray-module-belong-face)) ("clock" . (awesome-tray-module-clock-info awesome-tray-module-clock-face)) ("org-pomodoro" . (awesome-tray-module-org-pomodoro-info awesome-tray-module-org-pomodoro-face)) ("pdf-view-page" . (awesome-tray-module-pdf-view-page-info awesome-tray-module-pdf-view-page-face)) ("flymake" . (awesome-tray-module-flymake-info nil)) ("flycheck" . (awesome-tray-module-flycheck-info nil)) ("meow" . (awesome-tray-module-meow-info awesome-tray-module-meow-face)) ("mpd" . (awesome-tray-module-mpd-info awesome-tray-module-mpd-face)) ("volume" . (awesome-tray-module-volume-info awesome-tray-module-volume-face)) ("word-count" . (awesome-tray-module-word-count-info awesome-tray-module-word-count-face)) ("anzu" . (awesome-tray-module-anzu-info awesome-tray-module-anzu-face)) ("github" . (awesome-tray-module-github-info awesome-tray-module-github-face)) ("hostname" . (awesome-tray-module-hostname-info awesome-tray-module-hostname-face)))) (with-eval-after-load 'mu4e-alert (add-hook 'mu4e-index-updated-hook #'mu4e-alert-update-mail-count-modeline) (add-hook 'mu4e-message-changed-hook #'mu4e-alert-update-mail-count-modeline) (advice-add #'mu4e-context-switch :around #'mu4e-alert--context-switch) (mu4e-alert-update-mail-count-modeline) (defun awesome-tray-module-mail-info () (if (member "all-the-icons" (font-family-list)) (concat (all-the-icons-material "mail" :v-adjust -0.1) ":" (substring mu4e-alert-mode-line 7 -2)) mu4e-alert-mode-line)) (add-to-list 'awesome-tray-module-alist '("mail" . (awesome-tray-module-mail-info awesome-tray-module-belong-face)))) (defun awesome-tray-module-clock-info () (if (org-clocking-p) (format " [%s] (%s)" (org-duration-from-minutes (floor (org-time-convert-to-integer (org-time-since org-clock-start-time)) 60)) org-clock-heading))) (defun awesome-tray-module-github-info () (let ((current-seconds (awesome-tray-current-seconds))) (if (> (- current-seconds awesome-tray-github-last-time) awesome-tray-github-update-duration) (progn (setq awesome-tray-github-last-time current-seconds) (awesome-tray--github-fetch-notifications)) (if (> (- current-seconds awesome-tray-github-last-time) awesome-tray-github-erase-duration) (setq awesome-tray--github-notification-number 0)) (if (and (numberp awesome-tray--github-notification-number) (> awesome-tray--github-notification-number 0)) (format "%s github" awesome-tray--github-notification-number) "")))) (defun awesome-tray--github-fetch-notifications () "Fetch GitHub notifications." (when (require 'async nil t) (async-start `(lambda () ,(async-inject-variables "\\`\\(load-path\\|auth-sources\\|awesome-tray-before-github-fetch-notification-hook\\)\\'") (run-hooks 'awesome-tray-before-github-fetch-notification-hook) (when (require 'ghub nil t) (with-timeout (10) (ignore-errors (when-let* ((username (ghub--username ghub-default-host)) (token (ghub--token ghub-default-host username 'ghub t))) (ghub-get "/notifications" nil :query '((notifications . "true")) :username username :auth token :noerror t)))))) (lambda (result) (message "") ; suppress message (setq awesome-tray--github-notification-number (length result)))))) (defun awesome-tray-module-hostname-info () "Hostname for remote buffers." (when default-directory (when-let ((host (file-remote-p default-directory 'host))) (concat "@" host)))) (defun awesome-tray-module-word-count-info () (let ((f-count (count-words (point-min) (point-max)))) (if (region-active-p) (format "%d/%dW" (count-words-region (region-beginning) (region-end)) f-count) (format "%dW" f-count)))) (with-eval-after-load 'anzu (add-hook 'isearch-mode-end-hook #'anzu--reset-status t) (add-hook 'iedit-mode-end-hook #'anzu--reset-status) (advice-add #'evil-force-normal-state :after #'anzu--reset-status) ;; Fix matches segment mirroring across all buffers (mapc #'make-variable-buffer-local '(anzu--total-matched anzu--current-position anzu--state anzu--cached-count anzu--cached-positions anzu--last-command anzu--last-isearch-string anzu--overflow-p))) (defun awesome-tray--fix-anzu-count (positions here) "Calulate anzu count via POSITIONS and HERE." (cl-loop for (start . end) in positions collect t into before when (and (>= here start) (<= here end)) return (length before) finally return 0)) (defun awesome-tray-module-anzu-info () "Show the match index and total number thereof. Requires `anzu', also `evil-anzu' if using `evil-mode' for compatibility with `evil-search'." (if (featurep 'anzu) (when (and (bound-and-true-p anzu--state) (not (bound-and-true-p iedit-mode))) (let ((here anzu--current-position) (total anzu--total-matched)) (cond ((eq anzu--state 'replace-query) (format "%d replace" anzu--cached-count)) ((eq anzu--state 'replace) (format "[%d/%d]" here total)) (anzu--overflow-p (format "%s+" total)) (t (format "[%s/%d]" here total))))) "")) (defun awesome-tray-build-active-info () (condition-case nil (mapconcat 'identity (cl-remove-if #'(lambda (n) (equal (length n) 0)) (mapcar 'awesome-tray-get-module-info awesome-tray-active-modules)) awesome-tray-separator) (format "Awesome Tray broken."))) (defun awesome-tray-build-essential-info () (condition-case nil (mapconcat 'identity (cl-remove-if #'(lambda (n) (equal (length n) 0)) (mapcar 'awesome-tray-get-module-info awesome-tray-essential-modules)) awesome-tray-separator) (format "Awesome Tray broken."))) (defun awesome-tray-get-module-info (module-name) (let* ((func (ignore-errors (cadr (assoc module-name awesome-tray-module-alist)))) (face-param (ignore-errors (caddr (assoc module-name awesome-tray-module-alist)))) (face (cond ((functionp face-param) (funcall face-param)) ((facep face-param) face-param) (t nil))) (raw-info (ignore-errors (funcall func))) (info (ignore-errors (if face (propertize raw-info 'face face) raw-info)))) (if func (if info info (propertize "" 'face face)) (propertize module-name 'face 'awesome-tray-default-face)))) (defun awesome-tray-module-volume-info () (if (ignore-errors (require 'volume)) (concat (number-to-string (truncate (volume-get))) "%") "")) (defun awesome-tray-module-git-info () (if (executable-find "git") (progn (if (not (string= (buffer-file-name) awesome-tray-git-buffer-filename)) (awesome-tray-git-command-update-cache)) awesome-tray-git-command-cache) "")) (defun awesome-tray-git-command-update-cache () (if (file-exists-p (format "%s" (buffer-file-name))) (let* ((filename (buffer-file-name)) (status (vc-git-state filename)) (branch (car (ignore-errors (vc-git-branches))))) (pcase status ('up-to-date (setq status "")) ('edited (setq status "!")) ('needs-update (setq status "⇣")) ('needs-merge (setq status "⇡")) ('unlocked-changes (setq status "")) ('added (setq status "+")) ('removed (setq status "-")) ('conflict (setq status "=")) ('missing (setq status "?")) ('ignored (setq status "")) ('unregistered (setq status "?")) (_ (setq status ""))) (if (not branch) (setq branch "")) (setq awesome-tray-git-buffer-filename filename) (setq awesome-tray-git-command-cache (if awesome-tray-git-show-status (format awesome-tray-git-format (string-trim (concat branch " " status))) (format awesome-tray-git-format branch)))) (setq awesome-tray-git-buffer-filename nil awesome-tray-git-command-cache ""))) (defun awesome-tray-module-circe-info () "Display circe tracking buffers" (if (listp tracking-mode-line-buffers) (apply 'concat (cl-loop for entry in tracking-mode-line-buffers collect (or (plist-get entry :propertize) ""))) "")) (defun awesome-tray-module-rvm-info () (if (executable-find "rvm-prompt") (format "rvm:%s" (replace-regexp-in-string "\n" "" (nth 1 (awesome-tray-process-exit-code-and-output "rvm-prompt")))) "")) (defun awesome-tray-module-battery-info () (let ((current-seconds (awesome-tray-current-seconds))) (if (> (- current-seconds awesome-tray-battery-status-last-time) awesome-tray-battery-update-duration) (let* ((battery-info (funcall battery-status-function)) (battery-type (battery-format "%L" battery-info)) battery-status) (setq awesome-tray-battery-status-last-time current-seconds) ;; Short battery type. (cond ((member battery-type '("on-line" "AC")) (setq battery-type "AC") (setq battery-status "")) ((member battery-type '("off-line" "BAT" "Battery")) (setq battery-type "") (setq battery-status (battery-format (if (eq system-type 'darwin) "[%p%%]" "[%p%% %t]") battery-info)))) ;; Update battery cache. (setq awesome-tray-battery-status-cache (concat battery-type battery-status))) awesome-tray-battery-status-cache))) (defun awesome-tray-module-mode-name-info () (car (split-string (format "%s" major-mode) "-mode"))) (defun awesome-tray-module-location-info () (if (equal major-mode 'eaf-mode) "" (string-replace " All" awesome-tray-location-info-all (string-replace " Top" awesome-tray-location-info-top (string-replace " Bottom" awesome-tray-location-info-bottom (format-mode-line awesome-tray-location-format)))))) (defun awesome-tray-module-location-or-page-info () "Show Location or PDF page depends on current mode." (let ((page-info (awesome-tray-module-pdf-view-page-info))) (if (string= page-info "") (awesome-tray-module-location-info) page-info))) (with-eval-after-load 'libmpdel (add-hook 'libmpdel-current-playlist-changed-hook 'awesome-tray-mpd-command-update-cache) (add-hook 'libmpdel-current-song-changed-hook 'awesome-tray-mpd-command-update-cache)) (defun awesome-tray-module-mpd-info () (if (and (ignore-errors (require 'libmpdel)) (executable-find "mpd")) (if (libmpdel-connected-p) awesome-tray-mpd-command-cache "not connected to mpd") "")) (defun awesome-tray-mpd-command-update-cache () (let* ((mpd-info (libmpdel-current-song)) (title (or (libmpdel-entity-name mpd-info) "")) (artist (or (libmpdel-artist-name mpd-info) "")) (album (or (libmpdel-album-name mpd-info) "")) (position (or (+ (libmpdel-song-position mpd-info) 1) "")) (playlist-length (or (libmpdel-playlist-length) "")) (filename (or (libmpdel-song-file mpd-info) ""))) (setq title (awesome-tray-truncate-string title awesome-tray-mpd-title-max-length)) (setq cut-filename (awesome-tray-truncate-string (file-name-sans-extension (replace-regexp-in-string ".*/" "" filename)) awesome-tray-mpd-title-max-length)) (setq filename (awesome-tray-truncate-string filename awesome-tray-mpd-title-max-length)) (setq awesome-tray-mpd-command-cache (format-spec awesome-tray-mpd-format (format-spec-make ?t title ?a artist ?A album ?p position ?P playlist-length ?f cut-filename ?F filename))))) (defun awesome-tray-module-date-info () "Displays the date." (format-time-string awesome-tray-date-format)) (defun awesome-tray-module-celestial-info () "Displays lunar phase and sunrise/sunset time." (with-demoted-errors "" (if (featurep 'celestial-mode-line) (substring-no-properties celestial-mode-line-string) ""))) (defun awesome-tray-module-last-command-info () ;; Only show last command when user enable `toggle-debug-on-error'. (when debug-on-error (format "%s" last-command))) (defun awesome-tray-module-buffer-name-info () (let (bufname) (setq bufname (if awesome-tray-buffer-name-buffer-changed (if (and (buffer-modified-p) (not (eq buffer-file-name nil))) (concat (buffer-name) awesome-tray-buffer-name-buffer-changed-style) (buffer-name)) (format "%s" (buffer-name)))) (awesome-tray-truncate-string bufname awesome-tray-buffer-name-max-length t))) (defun awesome-tray-module-buffer-read-only-info () (if (and (eq buffer-read-only t) (not (eq buffer-file-name nil))) (format "%s" awesome-tray-buffer-read-only-style))) (defun awesome-tray-module-input-method-info () (format "%s" (if (eq current-input-method nil) awesome-tray-input-method-default-style (if (member current-input-method awesome-tray-input-method-local-methods) awesome-tray-input-method-local-style current-input-method-title)))) (defun awesome-tray-module-parent-dir-info () (format "%s" (file-name-nondirectory (directory-file-name default-directory)))) (defun awesome-tray-shrink-dir-name (input-string) (let* ((words (split-string input-string "-")) (abbreviated-words (mapcar (lambda (word) (substring word 0 (min awesome-tray-file-path-truncated-name-length (length word)))) words))) (mapconcat 'identity abbreviated-words "-"))) (defun awesome-tray-module-file-path-info () (let* ((file-path (split-string (if buffer-file-name buffer-file-name default-directory) "/" t)) (full-num awesome-tray-file-path-full-dirname-levels) (show-name awesome-tray-file-path-show-filename) shown-path) ;; Remove file name if `awesome-tray-file-path-show-filename' is nil. (setq show-path (if buffer-file-name (if show-name file-path (butlast file-path)) file-path)) ;; Remove redundant directory with `awesome-tray-file-path-full-dirname-levels' value. (setq show-path (nthcdr (- (length show-path) (if buffer-file-name (if show-name (1+ full-num) full-num) (1+ full-num))) show-path)) ;; Shrink parent directory name to save minibuffer space. (setq show-path (append (mapcar #'awesome-tray-shrink-dir-name (butlast show-path)) (last show-path))) ;; Join paths. (setq show-path (mapconcat #'identity show-path "/")) show-path)) (defun awesome-tray-module-awesome-tab-info () (with-demoted-errors "" (if (featurep 'awesome-tab) (let ((tab-info (format "%s" (cdr (awesome-tab-selected-tab (awesome-tab-current-tabset t)))))) (if (> (string-width tab-info) 30) "" tab-info)) ""))) (defun awesome-tray-module-evil-info () (with-demoted-errors "" (if (featurep 'evil) (let ((state (cond ((not awesome-tray-evil-show-mode) "") ((evil-normal-state-p) "") ((evil-emacs-state-p) "") ((evil-insert-state-p) "") ((evil-motion-state-p) "") ((evil-visual-state-p) "") ((evil-operator-state-p) "") ((evil-replace-state-p) "") (t "")))) (if awesome-tray-evil-show-macro (setq state (string-trim (concat (awesome-tray--macro-recording) " " state)))) (if awesome-tray-evil-show-cursor-count (setq state (string-trim (concat (awesome-tray--count-multiple-cursors) " " state)))) state) ""))) (defun awesome-tray-module-meow-info () (with-demoted-errors "" (if (and (featurep 'meow) awesome-tray-meow-show-mode) meow--indicator ""))) (defun awesome-tray--macro-recording () "Display current evil macro being recorded." (if (featurep 'evil) (when (or defining-kbd-macro executing-kbd-macro) (if (bound-and-true-p evil-this-macro) (format "recording @%s" (char-to-string evil-this-macro)) "Macro")) "")) (defun awesome-tray--count-multiple-cursors () "Show the number of multiple cursors." (let (count) (cond ((featurep 'evil-mc) (if (bound-and-true-p evil-mc-cursor-list) (format "mc:%s" (number-to-string (+ (length evil-mc-cursor-list) 1))))) ((featurep 'multiple-cursors) (if (bound-and-true-p multiple-cursors-mode) (format "mc:%s" (number-to-string (mc/num-cursors))))) (t "")))) (defun awesome-tray-module-belong-info () (if (featurep 'treesit) (let ((current-seconds (awesome-tray-current-seconds))) (if (or (not (eq (current-buffer) awesome-tray-belong-last-buffer)) (> (- current-seconds awesome-tray-belong-last-time) awesome-tray-belong-update-duration)) (progn (setq awesome-tray-belong-last-time current-seconds) (setq awesome-tray-belong-last-buffer (current-buffer)) (awesome-tray-update-belong-cache)) awesome-tray-belong-cache)) "")) (defun awesome-tray-update-belong-cache () (setq awesome-tray-belong-cache (let* ((class-nodes (append (awesome-tray-get-match-nodes '((class_definition name: (symbol) @x))) (awesome-tray-get-match-nodes '((class_definition name: (identifier) @x))) (awesome-tray-get-match-nodes '((class_declaration name: (identifier) @x))) (awesome-tray-get-match-nodes '((class_specifier name: (type_identifier) @x))))) (function-nodes (append (awesome-tray-get-match-nodes '((function_definition name: (symbol) @x))) (awesome-tray-get-match-nodes '((function_definition name: (identifier) @x))) (awesome-tray-get-match-nodes '((function_declarator declarator: (identifier) @x))) (awesome-tray-get-match-nodes '((method_declaration name: (identifier) @x))) (awesome-tray-get-match-nodes '((function_declarator declarator: (field_identifier) @x))))) which-belong-info which-class-info which-func-info) ;; Set class information if point at between range of parent node. (setq which-class-info (catch 'found (dolist (class-node class-nodes) (let ((node (car class-node))) (pcase (format "%s" (cdr class-node)) ("(awesome-tray-get-match-nodes '((class_specifier name: (type_identifier) @x)))" (when (and (> (point) (treesit-node-start node)) (< (point) (treesit-node-end node))) (throw 'found (treesit-node-text node)))) (t (when (and (> (point) (treesit-node-start (treesit-node-parent node))) (< (point) (treesit-node-end (treesit-node-parent node)))) (throw 'found (treesit-node-text node))))))) (throw 'found ""))) ;; Set function information if point at between range of parent node. (setq which-func-info (catch 'found (dolist (function-node function-nodes) (let ((node (car function-node))) (pcase (format "%s" (cdr function-node)) ("((function_declarator declarator: (identifier) @x))" (when (and (> (point) (treesit-node-start (treesit-node-parent (treesit-node-parent node)))) (< (point) (treesit-node-end (treesit-node-parent (treesit-node-parent node))))) (throw 'found (treesit-node-text node)))) ("((function_declarator declarator: (field_identifier) @x))" (when (and (> (point) (treesit-node-start (treesit-node-parent node))) (< (point) (treesit-node-end (treesit-node-parent node)))) (throw 'found (treesit-node-text node)))) (t (when (and (> (point) (treesit-node-start (treesit-node-parent node))) (< (point) (treesit-node-end (treesit-node-parent node)))) (throw 'found (treesit-node-text node))))))) (throw 'found ""))) (setq which-belong-info (string-trim (concat which-class-info " " which-func-info))) (if (string-equal which-belong-info "") "" (format "[%s]" which-belong-info)))) awesome-tray-belong-cache) (defun awesome-tray-module-org-pomodoro-info () (with-demoted-errors "" (if (featurep 'org-pomodoro) (let ((state (cond ((eq org-pomodoro-state :pomodoro) (concat "[" (org-pomodoro-format-seconds) "]")) ((eq org-pomodoro-state :short-break) (concat "(" (org-pomodoro-format-seconds) ")")) ((eq org-pomodoro-state :long-break) (concat "{" (org-pomodoro-format-seconds) "}")) (t "")))) state) ""))) (defun awesome-tray-module-pdf-view-page-info () (with-demoted-errors "" (cond ((and (derived-mode-p 'eaf-mode) (string-equal eaf--buffer-app-name "pdf-viewer")) (eaf-call-sync "execute_function" eaf--buffer-id "get_progress")) ((featurep 'pdf-view) (let ((state (cond ((derived-mode-p 'pdf-view-mode) (format "%d/%d" (eval '(pdf-view-current-page)) (pdf-cache-number-of-pages))) (t "")))) state)) (t "")))) (defun awesome-tray-module-flymake-info () "A module for showing Flymake state." ;; Parts of the code are from doom-modeline package (with-demoted-errors "" (if (and (featurep 'flymake) flymake--state) (let* ((known (hash-table-keys flymake--state)) (running (flymake-running-backends)) (disabled (flymake-disabled-backends)) (reported (flymake-reporting-backends)) (disabledp (and disabled (null running))) (waiting (cl-set-difference running reported))) (when-let ((flymake-state (cond (waiting "⏳") ((null known) "❔") (disabledp "❕") (t (let ((.error 0) (.warning 0) (.note 0)) (cl-loop with warning-level = (warning-numeric-level :warning) with note-level = (warning-numeric-level :debug) for state being the hash-values of flymake--state do (cl-loop with diags = (flymake--state-diags state) for diag in diags do (let ((severity (flymake--lookup-type-property (flymake--diag-type diag) 'severity (warning-numeric-level :error)))) (cond ((> severity warning-level) (cl-incf .error)) ((> severity note-level) (cl-incf .warning)) (t (cl-incf .note)))))) (let ((num (+ .error .warning .note))) (if (> num 0) (string-clean-whitespace (string-join (list (when (> .note 0) (concat "🔵:" (propertize (number-to-string .note) 'face 'awesome-tray-module-flymake-note))) (when (> .warning 0) (concat "🟠:" (propertize (number-to-string .warning) 'face 'awesome-tray-module-flymake-warning))) (when (> .error 0) (concat "🔴:" (propertize (number-to-string .error) 'face 'awesome-tray-module-flymake-error)))) " ")) "🟢"))))))) flymake-state))))) (with-eval-after-load 'flycheck (add-hook 'flycheck-status-changed-functions 'awesome-tray-flycheck-status-update-cache) (add-hook 'window-buffer-change-functions (lambda (_) (awesome-tray-flycheck-status-update-cache))) ) (defun awesome-tray-flycheck-status-update-cache (&optional status) (if (or status flycheck-last-status-change) (let* ((status (symbol-name flycheck-last-status-change)) (status (pcase status ;;("not-checked" "💤") ;;("no-checker" "❌") ("running" "⏳") ("errored" "⚠️") ("finished" "") ("interrupted" "⏸️") ("suspicious" "🤔") (_ status))) (counts (flycheck-count-errors flycheck-current-errors))) ;;(debug) (setq awesome-tray-flycheck-status-cache (if (not (member status '("not-checked" "no-checker"))) (let ((errors (cdr (assq 'error counts))) (warnings (cdr (assq 'warning counts)))) (format "%s 🔴:%s|🟠:%s" status (propertize (number-to-string (or errors 0)) 'face 'awesome-tray-module-flymake-error) (propertize (number-to-string (or warnings 0)) 'face 'awesome-tray-module-flymake-warning))) "" )) ;;(message awesome-tray-flycheck-status-cache) ) (setq awesome-tray-flycheck-status-cache "") ) ) (defun awesome-tray-module-flycheck-info () (when (featurep 'flycheck) awesome-tray-flycheck-status-cache ) ) (defun awesome-tray-get-match-nodes (query) (ignore-errors (mapcar #'(lambda (range) (cons (treesit-node-at (car range)) query)) (treesit-query-range (treesit-node-language (treesit-buffer-root-node)) query)))) (defun awesome-tray-truncate-string (string length &optional right) "Truncate STRING to LENGTH, replacing the surplus with an ellipsis. If right is non nil, replace to the right" (let ((strlen (length string))) (if (<= strlen length) string (setq length (max 0 (- length (length awesome-tray-ellipsis)))) (if right (format "%s%s" (substring string 0 length) awesome-tray-ellipsis) (format "%s%s" awesome-tray-ellipsis (substring string (max 0 (- strlen length)))))))) (defun awesome-tray-get-frame-width () "Only calculating a main Frame width, to avoid wrong width when new frame, such as `snails'." ;; `window-width' can calculate the width of the minibuffer more accurately than `frame-width'. ;; We need remove `awesome-tray-info-padding-right' (if (display-graphic-p) (with-selected-frame (if (daemonp) (car (last (butlast (frame-list)))) (car (last (frame-list)))) (window-width (minibuffer-window))) (window-width (minibuffer-window)))) (defun awesome-tray-process-exit-code-and-output (program &rest args) "Run PROGRAM with ARGS and return the exit code and output in a list." (with-temp-buffer (list (apply 'call-process program nil (current-buffer) nil args) (buffer-string)))) (defun awesome-tray-current-seconds () (string-to-number (format-time-string "%s"))) ;;;###autoload (define-minor-mode awesome-tray-mode "Display text at the end of the echo area." :global t (if awesome-tray-mode (awesome-tray-enable) (awesome-tray-disable))) ;;;###autoload (defun awesome-tray-enable () "Turn on the awesome-tray." (interactive) ;; Disable any existing awesome-tray to remove conflicts (awesome-tray-disable) (when awesome-tray-hide-mode-line ;; Save mode-line colors when first time. ;; Don't change `awesome-tray-mode-line-colors' anymore. (unless awesome-tray-mode-line-colors (setq awesome-tray-mode-line-colors (list (face-attribute 'mode-line :foreground) (face-attribute 'mode-line :background) (face-attribute 'mode-line :family) (face-attribute 'mode-line :box) (face-attribute 'mode-line-inactive :foreground) (face-attribute 'mode-line-inactive :background) (face-attribute 'mode-line-inactive :family) (face-attribute 'mode-line-inactive :box)))) (setq awesome-tray-mode-line-default-height (face-attribute 'mode-line :height)) ;; Disable mode line. (set-face-attribute 'mode-line nil :foreground awesome-tray-mode-line-active-color :background awesome-tray-mode-line-active-color :height awesome-tray-mode-line-height :box nil) (set-face-attribute 'mode-line-inactive nil :foreground awesome-tray-mode-line-inactive-color :background awesome-tray-mode-line-inactive-color :height awesome-tray-mode-line-height :box nil :inherit 'unspecified)) ;; Create overlays in each echo area buffer (dolist (buf '(" *Echo Area 0*" " *Echo Area 1*")) (with-current-buffer (get-buffer-create buf) (remove-overlays (point-min) (point-max)) (push (make-overlay (point-min) (point-max) nil nil t) awesome-tray-overlays))) ;; Start the timer to automatically update (when awesome-tray-update-interval (run-with-timer 0 awesome-tray-update-interval 'awesome-tray-update)) ;; Add the setup function to the minibuffer hook (when awesome-tray-minibuffer (add-hook 'minibuffer-setup-hook #'awesome-tray--minibuffer-setup)) ;; Add git hooks (if (or (member "git" awesome-tray-active-modules) (member "git" awesome-tray-essential-modules)) (dolist (hook awesome-tray-git-update-hooks) (add-hook hook 'awesome-tray-git-command-update-cache))) ;; Add anzu advice (if (or (member "anzu" awesome-tray-active-modules) (member "anzu" awesome-tray-essential-modules)) (progn (advice-add #'anzu--where-is-here :override #'awesome-tray--fix-anzu-count) ;; manage modeline segment ourselves (setq anzu-cons-mode-line-p nil)))) ;;;###autoload (defun awesome-tray-disable () "Turn off the awesome-tray." (interactive) (when awesome-tray-hide-mode-line ;; Restore mode-line colors. (when awesome-tray-mode-line-colors (set-face-attribute 'mode-line nil :foreground (nth 0 awesome-tray-mode-line-colors) :background (nth 1 awesome-tray-mode-line-colors) :family (nth 2 awesome-tray-mode-line-colors) :box (nth 3 awesome-tray-mode-line-colors) :height awesome-tray-mode-line-default-height) (set-face-attribute 'mode-line-inactive nil :foreground (nth 4 awesome-tray-mode-line-colors) :background (nth 5 awesome-tray-mode-line-colors) :family (nth 6 awesome-tray-mode-line-colors) :box (nth 7 awesome-tray-mode-line-colors) :height awesome-tray-mode-line-default-height))) ;; Remove awesome-tray overlays (mapc 'delete-overlay awesome-tray-overlays) (setq awesome-tray-overlays nil) ;; Remove text from Minibuf-0 (with-current-buffer " *Minibuf-0*" (delete-region (point-min) (point-max))) ;; Cancel the update timer (cancel-function-timers #'awesome-tray-update) ;; Remove the setup function from the minibuffer hook (remove-hook 'minibuffer-setup-hook #'awesome-tray--minibuffer-setup) ;; Remove git hooks (dolist (hook awesome-tray-git-update-hooks) (remove-hook hook 'awesome-tray-git-command-update-cache))) (defun awesome-tray-is-rime-display-in-minibuffer () (if (and (featurep 'rime) (eq rime-show-candidate 'minibuffer)) rime--current-input-key nil)) (defun awesome-tray-set-text (text) "Set the text displayed by the awesome-tray to TEXT." ;; Only set tray information when minibuffer not in `input' state. ;; Don't fill tray information if user is typing in minibuffer. (unless (or (active-minibuffer-window) (awesome-tray-is-rime-display-in-minibuffer)) (let* ((wid (+ (string-width text) awesome-tray-info-padding-right)) (spc (pcase awesome-tray-position ('center (propertize " " 'cursor 1 'display `(space :align-to (- center ,(/ wid 2))))) ('left (propertize " " 'cursor 1 'display `(space :align-to (- left-fringe ,wid)))) ('right (propertize " " 'cursor 1 'display `(space :align-to (- right-fringe ,(if (display-graphic-p) (if (= (nth 1 (window-fringes)) 0) ;; no right fringe, need 1 char padding to avoid ;; line wrap 1 (if (and (not overflow-newline-into-fringe) (= awesome-tray-info-padding-right 0)) ;; need 1 pixel padding to avoid line wrap when ;; overflow-newline-into-fringe is nil '(1) '(0))) (if (= awesome-tray-info-padding-right 0) ;; need 1 char in TUI to avoid line wrap 1 0)) ,wid))))))) (setq awesome-tray-text (concat (if awesome-tray-second-line "\n") spc text)) ;; Remove any dead overlays from the minibuffer from the beginning of the list (while (null (overlay-buffer (car awesome-tray-overlays))) (pop awesome-tray-overlays)) ;; Add the correct text to each awesome-tray overlay (dolist (o awesome-tray-overlays) (when (overlay-buffer o) (overlay-put o 'after-string awesome-tray-text))) ;; Display the text in Minibuf-0 (with-current-buffer " *Minibuf-0*" (delete-region (point-min) (point-max)) (insert awesome-tray-text))))) (defun awesome-tray--minibuffer-setup () "Setup the awesome-tray in the minibuffer." (push (make-overlay (point-max) (point-max) nil t t) awesome-tray-overlays) (overlay-put (car awesome-tray-overlays) 'priority 1) (awesome-tray-update)) (defun awesome-tray-adjust-mode-line-color () (let ((mode-line-color (cond ;; Use `awesome-tray-mode-line-active-color' when current buffer is minibuffer or blink-search input buffer. ((or (minibufferp) (member (buffer-name (current-buffer)) '(" *blink search input*"))) awesome-tray-mode-line-active-color) ;; Use `awesome-tray-mode-line-modified-readonly-color' if buffer is modified and read-only. ((and (buffer-modified-p) buffer-read-only) awesome-tray-mode-line-modified-readonly-color) ;; Use `awesome-tray-mode-line-readonly-color' if buffer is read-only. (buffer-read-only awesome-tray-mode-line-readonly-color) ;; Use `awesome-tray-mode-line-active-color' if `auto-save' plugin is enable, avoid blink eyes. ((buffer-modified-p) (if (require 'auto-save nil t) awesome-tray-mode-line-active-color awesome-tray-mode-line-modified-color)) (t awesome-tray-mode-line-active-color)))) (set-face-attribute 'mode-line nil :foreground mode-line-color :background mode-line-color))) (defun awesome-tray-update () "Get new text to be displayed." (interactive) ;; Adjust mode-line color with buffer's state. (when awesome-tray-adjust-mode-line-color-enable (awesome-tray-adjust-mode-line-color)) (let* ((tray-active-info (awesome-tray-build-active-info)) ;; Get minibuffer content. (echo-message (current-message)) ;; Remove text property from content. (_ (set-text-properties 0 (length echo-message) nil echo-message)) ;; Set empty string if `echo-message' not string. (minibuffer-info (if (stringp echo-message) echo-message "")) ;; Only fetch last line from content to calculate the width of left side minibuffer. (minibuffer-last-line (car (last (split-string minibuffer-info "\n")))) ;; Calculate blank length between message and active tray info. (blank-length (- (awesome-tray-get-frame-width) (string-width tray-active-info) (string-width minibuffer-last-line)))) (awesome-tray-set-text (if (> blank-length 0) ;; Show active tray info if have blank. tray-active-info ;; Otherwise show essential tray info. (awesome-tray-build-essential-info))))) (provide 'awesome-tray) ;;; awesome-tray.el ends here