diff --git a/mood-line.el b/mood-line.el index 0907561..ced21a0 100644 --- a/mood-line.el +++ b/mood-line.el @@ -18,6 +18,7 @@ ;; * Anzu and multiple-cursors counter ;; * Version control status indicator ;; * Flycheck status indicator +;; * Flymake support ;; * Lightweight with no dependencies ;; ;; To enable mood-line: @@ -46,9 +47,13 @@ ;; Variable declarations ;; -(defvar mood-line--current-window) (defvar flycheck-current-errors) +(defvar flymake--mode-line-format) (defvar anzu--state) +(defvar anzu--cached-count) +(defvar anzu--overflow-p) +(defvar anzu--current-position) +(defvar anzu--total-matched) (defvar multiple-cursors-mode) ;; @@ -56,7 +61,6 @@ ;; (declare-function flycheck-count-errors "flycheck" (errors)) -(declare-function anzu--update-mode-line "anzu" ()) (declare-function mc/num-cursors "multiple-cursors" ()) ;; @@ -67,13 +71,23 @@ "A minimal mode-line configuration inspired by doom-modeline." :group 'mode-line) -(defcustom mood-line-show-point nil +(defcustom mood-line-show-eol-style nil + "If t, the EOL style of the current buffer will be displayed in the mode-line." + :group 'mood-line + :type 'boolean) + +(defcustom mood-line-show-encoding-information nil + "If t, the encoding format of the current buffer will be displayed in the mode-line." + :group 'mood-line + :type 'boolean) + +(defcustom mood-line-show-cursor-point nil "If t, the value of `point' will be displayed next to the cursor position in the mode-line." :group 'mood-line :type 'boolean) -(defface mood-line-status-grayed-out - '((t (:inherit (font-lock-doc-face)))) +(defface mood-line-status-neutral + '((t (:inherit (shadow)))) "Face used for neutral or inactive status indicators in the mode-line." :group 'mood-line) @@ -98,11 +112,10 @@ :group 'mood-line) (defface mood-line-unimportant - '((t (:inherit (font-lock-doc-face)))) + '((t (:inherit (shadow)))) "Face used for less important mode-line elements." :group 'mood-line) - (defface mood-line-modified '((t (:inherit (error)))) "Face used for the 'modified' indicator symbol in the mode-line." @@ -112,36 +125,35 @@ ;; Helper functions ;; -(defun mood-line-format (left right) +(defun --string-trim-left (string) + "Remove whitespace at the beginning of STRING." + (if (string-match "\\`[ \t\n\r]+" string) + (replace-match "" t t string) + string)) + +(defun --string-trim-right (string) + "Remove whitespace at the end of STRING." + (if (string-match "[ \t\n\r]+\\'" string) + (replace-match "" t t string) + string)) + +(defun --string-trim (string) + "Remove whitespace at the beginning and end of STRING." + (--string-trim-left (--string-trim-right string))) + +(defun --format-mood-line (left right) "Return a string of `window-width' length containing LEFT and RIGHT, aligned respectively." (let ((reserve (length right))) - (when (and (display-graphic-p) (eq 'right (get-scroll-bar-mode))) - (setq reserve (- reserve 3))) - (concat - left - " " - (propertize " " - 'display `((space :align-to (- (+ right right-fringe right-margin) ,(+ reserve 0))))) - right))) - -;; Define a helper function to determine whether or not the current window is active. -(defsubst mood-line-is-active () - "Return \"t\" if the current window is active, \"nil\" if it is not." - (eq (selected-window) mood-line--current-window)) + (concat left + " " + (propertize " " + 'display `((space :align-to (- right ,reserve)))) + right))) ;; ;; Update functions ;; -;; Window update function -(defvar-local mood-line--current-window (frame-selected-window)) -(defun mood-line--update-selected-window (&rest _) - "Update the `mood-line--current-window' variable." - (when (frame-selected-window) - (let ((win (frame-selected-window))) - (unless (minibuffer-window-active-p win) - (setq mood-line--current-window win))))) - ;; VC update function (defvar-local mood-line--vc-text nil) (defun mood-line--update-vc-segment (&rest _) @@ -150,26 +162,25 @@ (when (and vc-mode buffer-file-name) (let ((backend (vc-backend buffer-file-name)) (state (vc-state buffer-file-name (vc-backend buffer-file-name)))) - (let ((face 'mode-line-inactive) - (active (mood-line-is-active))) + (let ((face 'mode-line-neutral)) (concat (cond ((memq state '(edited added)) - (if active (setq face 'mood-line-status-info)) - (propertize "✚" 'face face)) + (setq face 'mood-line-status-info) + (propertize "+ " 'face face)) ((eq state 'needs-merge) - (if active (setq face 'mood-line-status-warning)) - (propertize "●" 'face face)) + (setq face 'mood-line-status-warning) + (propertize "⟷ " 'face face)) ((eq state 'needs-update) - (if active (setq face 'mood-line-status-warning)) - (propertize "⬆" 'face face)) + (setq face 'mood-line-status-warning) + (propertize "↑ " 'face face)) ((memq state '(removed conflict unregistered)) - (if active (setq face 'mood-line-status-error)) - (propertize "✖" 'face face)) + (setq face 'mood-line-status-error) + (propertize "✖ " 'face face)) (t - (if active (setq face 'mood-line-status-grayed-out)) - (propertize "✔" 'face face))) - " " + (setq face 'mood-line-status-neutral) + (propertize "✔ " 'face face))) (propertize (substring vc-mode (+ (if (eq backend 'Hg) 2 3) 2)) - 'face (if active face)) + 'face face + 'mouse-face face) " ")))))) ;; Flycheck update function @@ -181,73 +192,74 @@ ('finished (if flycheck-current-errors (let-alist (flycheck-count-errors flycheck-current-errors) (let ((sum (+ (or .error 0) (or .warning 0)))) - (propertize (concat "✚ Issues: " + (propertize (concat "⚑ Issues: " (number-to-string sum) " ") 'face (if .error 'mood-line-status-error 'mood-line-status-warning)))) (propertize "✔ Good " 'face 'mood-line-status-success))) - ('running (propertize "● Checking " 'face 'mood-line-status-info)) - ('no-checker "") + ('running (propertize "𝚫 Checking " 'face 'mood-line-status-info)) ('errored (propertize "✖ Error " 'face 'mood-line-status-error)) - ('interrupted (propertize "⏸ Paused " 'face 'mood-line-status-grayed-out))))) + ('interrupted (propertize "⏸ Paused " 'face 'mood-line-status-neutral)) + ('no-checker "")))) ;; ;; Segments ;; (defun mood-line-segment-modified () - "Displays a color-coded buffer modification indicator in the mode-line." - (propertize - (if (and - (buffer-modified-p) - (not (string-match-p "\\*.*\\*" (buffer-name)))) - " ● " - " ") - 'face 'mood-line-modified)) + "Displays a color-coded buffer modification/read-only indicator in the mode-line." + (if (not (string-match-p "\\*.*\\*" (buffer-name))) + (if (buffer-modified-p) + (propertize "● " 'face 'mood-line-modified) + (if buffer-read-only + (propertize "■ " 'face 'mood-line-unimportant) + " ")) + " ")) (defun mood-line-segment-buffer-name () "Displays the name of the current buffer in the mode-line." - (concat (propertize "%b" 'face 'mode-line-buffer-id) " ")) + (propertize "%b " 'face 'mode-line-buffer-id)) (defun mood-line-segment-anzu () "Displays color-coded anzu status information in the mode-line (if available)." (when (and (boundp 'anzu--state) anzu--state) - (concat (anzu--update-mode-line) " "))) + (cond ((eq anzu--state 'replace-query) + (format #("Replace: %d " 0 11 (face mood-line-status-warning)) anzu--cached-count)) + (anzu--overflow-p + (format #("%d/%d+ " 0 3 (face mood-line-status-info) 3 6 (face mood-line-status-error)) anzu--current-position anzu--total-matched)) + (t + (format #("%d/%d " 0 5 (face mood-line-status-info)) anzu--current-position anzu--total-matched))))) (defun mood-line-segment-multiple-cursors () "Displays the number of active multiple-cursors in the mode-line (if available)." (when (and (boundp 'multiple-cursors-mode) multiple-cursors-mode) - (concat "MC:" - (format #("%d" 0 2 (face font-lock-warning-face)) (mc/num-cursors)) - " "))) + (concat "MC" + (format #("×%d " 0 3 (face mood-line-status-warning)) (mc/num-cursors))))) (defun mood-line-segment-position () "Displays the current cursor position in the mode-line." (concat "%l:%c" - (when mood-line-show-point - (concat ":" - (propertize (format "%d" (point)) 'face (if (mood-line-is-active) - 'mood-line-unimportant - 'mode-line-inactive)))) - " " - (propertize "%p%%" 'face (if (mood-line-is-active) - 'mood-line-unimportant - 'mode-line-inactive)) - " ")) + (when mood-line-show-cursor-point (propertize (format ":%d" (point)) 'face)) + (propertize " %p%% " 'face 'mood-line-unimportant))) + +(defun mood-line-segment-eol () + "Displays the EOL style of the current buffer in the mode-line." + (when mood-line-show-eol-style + (pcase (coding-system-eol-type buffer-file-coding-system) + (0 "LF ") + (1 "CRLF ") + (2 "CR ")))) (defun mood-line-segment-encoding () "Displays the encoding and EOL style of the buffer in the mode-line." - (concat (pcase (coding-system-eol-type buffer-file-coding-system) - (0 "LF ") - (1 "CRLF ") - (2 "CR ")) - (let ((sys (coding-system-plist buffer-file-coding-system))) - (cond ((memq (plist-get sys :category) '(coding-category-undecided coding-category-utf-8)) - "UTF-8") - (t (upcase (symbol-name (plist-get sys :name)))))) - " ")) + (when mood-line-show-encoding-information + (concat (let ((sys (coding-system-plist buffer-file-coding-system))) + (cond ((memq (plist-get sys :category) '(coding-category-undecided coding-category-utf-8)) + "UTF-8") + (t (upcase (symbol-name (plist-get sys :name)))))) + " "))) (defun mood-line-segment-vc () "Displays color-coded version control information in the mode-line." @@ -255,25 +267,27 @@ (defun mood-line-segment-major-mode () "Displays the current major mode in the mode-line." - (propertize "%m " - 'face (if (mood-line-is-active) - 'bold - 'mood-line-status-grayed-out))) + (propertize "%m " 'face 'bold)) -(defun mood-line-segment-global-mode-string () - "Displays the current value of `global-mode-string' in the mode-line." - (let ((global (format-mode-line global-mode-string 'mood-line-status-grayed-out))) - (unless (string-empty-p global) - (concat global " ")))) +(defun mood-line-segment-misc-info () + "Displays the current value of `mode-line-misc-info' in the mode-line." + (let ((misc-info (format-mode-line mode-line-misc-info 'mood-line-unimportant))) + (unless (string= misc-info "") + (concat (--string-trim misc-info) " ")))) (defun mood-line-segment-flycheck () "Displays color-coded flycheck information in the mode-line (if available)." mood-line--flycheck-text) +(defun mood-line-segment-flymake () + "Displays information about the current status of flymake in the mode-line (if available)." + (when (and (boundp 'flymake-mode) flymake-mode) + (concat (--string-trim (format-mode-line flymake--mode-line-format)) " "))) + (defun mood-line-segment-process () "Displays the current value of `mode-line-process' in the mode-line." (when mode-line-process - (list mode-line-process " "))) + (concat (--string-trim (format-mode-line mode-line-process)) " "))) ;; ;; Activation function @@ -300,19 +314,14 @@ (add-hook 'after-save-hook #'mood-line--update-vc-segment) (advice-add #'vc-refresh-state :after #'mood-line--update-vc-segment) - ;; Setup window update hooks - (add-hook 'window-configuration-change-hook #'mood-line--update-selected-window) - (add-hook 'focus-in-hook #'mood-line--update-selected-window) - (advice-add #'handle-switch-frame :after #'mood-line--update-selected-window) - (advice-add #'select-window :after #'mood-line--update-selected-window) - ;; Set the new mode-line-format (setq-default mode-line-format '((:eval - (mood-line-format + (--format-mood-line ;; Left (format-mode-line - '((:eval (mood-line-segment-modified)) + '(" " + (:eval (mood-line-segment-modified)) (:eval (mood-line-segment-buffer-name)) (:eval (mood-line-segment-anzu)) (:eval (mood-line-segment-multiple-cursors)) @@ -320,11 +329,13 @@ ;; Right (format-mode-line - '((:eval (mood-line-segment-encoding)) + '((:eval (mood-line-segment-eol)) + (:eval (mood-line-segment-encoding)) (:eval (mood-line-segment-vc)) (:eval (mood-line-segment-major-mode)) - (:eval (mood-line-segment-global-mode-string)) + (:eval (mood-line-segment-misc-info)) (:eval (mood-line-segment-flycheck)) + (:eval (mood-line-segment-flymake)) (:eval (mood-line-segment-process)) " "))))))) (progn @@ -338,12 +349,6 @@ (remove-hook 'after-save-hook #'mood-line--update-vc-segment) (advice-remove #'vc-refresh-state #'mood-line--update-vc-segment) - ;; Remove window update hooks - (remove-hook 'window-configuration-change-hook #'mood-line--update-selected-window) - (remove-hook 'focus-in-hook #'mood-line--update-selected-window) - (advice-remove #'handle-switch-frame #'mood-line--update-selected-window) - (advice-remove #'select-window #'mood-line--update-selected-window) - ;; Restore the original mode-line format (setq-default mode-line-format mood-line--default-mode-line))))