├── README.md └── mood-line.el /README.md: -------------------------------------------------------------------------------- 1 | # mood-line 2 | ### Version 1.2.4 3 | 4 | [![MELPA](https://melpa.org/packages/mood-line-badge.svg)](https://melpa.org/#/mood-line) 5 | [![MELPA Stable](https://stable.melpa.org/packages/mood-line-badge.svg)](https://stable.melpa.org/#/mood-line) 6 | 7 | ## About 8 | 9 | `mood-line` is a minimal mode-line configuration that aims to replicate some of the features of the 10 | [doom-modeline](https://github.com/seagle0128/doom-modeline) 11 | package. 12 | 13 | ## Features 14 | 15 | * Clean, minimal design 16 | 17 | * Anzu and multiple-cursors counters 18 | 19 | * Encoding and EOL style indicator 20 | 21 | * Version control status indicator 22 | 23 | * Flycheck status indicator 24 | 25 | * Flymake support 26 | 27 | * Lightweight with no dependencies 28 | 29 | ## Preview 30 | 31 | ![Preview Image](https://gitlab.com/jessieh/mood-line/raw/assets/mood-line.png "Preview Image") 32 | 33 | ## Installation 34 | 35 | To enable `mood-line`, place this in your configuration file after loading the package: 36 | 37 | `(mood-line-mode)` 38 | 39 | Disabling `mood-line` can be accomplished by toggling `mood-line-mode` off. 40 | 41 | ## Known Issues 42 | 43 | * No known issues. 44 | 45 | If you experience any issues with this package, please 46 | [open an issue](https://gitlab.com/jessieh/mood-line/issues/new) 47 | on the issue tracker. 48 | 49 | Suggestions for improvements and feature requests are always appreciated, as well! 50 | -------------------------------------------------------------------------------- /mood-line.el: -------------------------------------------------------------------------------- 1 | ;;; mood-line.el --- A minimal mode-line inspired by doom-modeline -*- lexical-binding: t; -*- 2 | 3 | ;; Author: Jessie Hildebrandt 4 | ;; Homepage: https://gitlab.com/jessieh/mood-line 5 | ;; Keywords: mode-line faces 6 | ;; Version: 1.2.4 7 | ;; Package-Requires: ((emacs "25.1")) 8 | 9 | ;; This file is not part of GNU Emacs. 10 | 11 | ;;; Commentary: 12 | ;; 13 | ;; mood-line is a minimal mode-line configuration that aims to replicate 14 | ;; some of the features of the doom-modeline package. 15 | ;; 16 | ;; Features offered: 17 | ;; * Clean, minimal design 18 | ;; * Anzu and multiple-cursors counter 19 | ;; * Version control status indicator 20 | ;; * Flycheck status indicator 21 | ;; * Flymake support 22 | ;; * Lightweight with no dependencies 23 | ;; 24 | ;; To enable mood-line: 25 | ;; (mood-line-mode) 26 | 27 | ;;; License: 28 | ;; 29 | ;; This program is free software; you can redistribute it and/or 30 | ;; modify it under the terms of the GNU General Public License as 31 | ;; published by the Free Software Foundation; either version 2, or 32 | ;; (at your option) any later version. 33 | ;; 34 | ;; This program is distributed in the hope that it will be useful, 35 | ;; but WITHOUT ANY WARRANTY; without even the implied warranty of 36 | ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 37 | ;; General Public License for more details. 38 | ;; 39 | ;; You should have received a copy of the GNU General Public License 40 | ;; along with this program; see the file COPYING. If not, write to 41 | ;; the Free Software Foundation, Inc., 51 Franklin Street, Fifth 42 | ;; Floor, Boston, MA 02110-1301, USA. 43 | 44 | ;;; Code: 45 | 46 | ;; 47 | ;; Variable declarations 48 | ;; 49 | 50 | (defvar flycheck-current-errors) 51 | (defvar flymake--mode-line-format) 52 | (defvar anzu--state) 53 | (defvar anzu--cached-count) 54 | (defvar anzu--overflow-p) 55 | (defvar anzu--current-position) 56 | (defvar anzu--total-matched) 57 | (defvar multiple-cursors-mode) 58 | 59 | ;; 60 | ;; Function prototypes 61 | ;; 62 | 63 | (declare-function flycheck-count-errors "flycheck" (errors)) 64 | (declare-function mc/num-cursors "multiple-cursors" ()) 65 | 66 | ;; 67 | ;; Config 68 | ;; 69 | 70 | (defgroup mood-line nil 71 | "A minimal mode-line configuration inspired by doom-modeline." 72 | :group 'mode-line) 73 | 74 | (defcustom mood-line-show-eol-style nil 75 | "If t, the EOL style of the current buffer will be displayed in the mode-line." 76 | :group 'mood-line 77 | :type 'boolean) 78 | 79 | (defcustom mood-line-show-encoding-information nil 80 | "If t, the encoding format of the current buffer will be displayed in the mode-line." 81 | :group 'mood-line 82 | :type 'boolean) 83 | 84 | (defcustom mood-line-show-cursor-point nil 85 | "If t, the value of `point' will be displayed next to the cursor position in the mode-line." 86 | :group 'mood-line 87 | :type 'boolean) 88 | 89 | (defface mood-line-buffer-name 90 | '((t (:inherit (mode-line-buffer-id)))) 91 | "Face used for major mode indicator in the mode-line." 92 | :group 'mood-line) 93 | 94 | (defface mood-line-major-mode 95 | '((t (:inherit (bold)))) 96 | "Face used for major mode indicator in the mode-line." 97 | :group 'mood-line) 98 | 99 | (defface mood-line-status-neutral 100 | '((t (:inherit (shadow)))) 101 | "Face used for neutral or inactive status indicators in the mode-line." 102 | :group 'mood-line) 103 | 104 | (defface mood-line-status-info 105 | '((t (:inherit (font-lock-keyword-face)))) 106 | "Face used for generic status indicators in the mode-line." 107 | :group 'mood-line) 108 | 109 | (defface mood-line-status-success 110 | '((t (:inherit (success)))) 111 | "Face used for success status indicators in the mode-line." 112 | :group 'mood-line) 113 | 114 | (defface mood-line-status-warning 115 | '((t (:inherit (warning)))) 116 | "Face for warning status indicators in the mode-line." 117 | :group 'mood-line) 118 | 119 | (defface mood-line-status-error 120 | '((t (:inherit (error)))) 121 | "Face for error stauts indicators in the mode-line." 122 | :group 'mood-line) 123 | 124 | (defface mood-line-unimportant 125 | '((t (:inherit (shadow)))) 126 | "Face used for less important mode-line elements." 127 | :group 'mood-line) 128 | 129 | (defface mood-line-modified 130 | '((t (:inherit (error)))) 131 | "Face used for the 'modified' indicator symbol in the mode-line." 132 | :group 'mood-line) 133 | 134 | ;; 135 | ;; Helper functions 136 | ;; 137 | 138 | (defun mood-line--string-trim-left (string) 139 | "Remove whitespace at the beginning of STRING." 140 | (if (string-match "\\`[ \t\n\r]+" string) 141 | (replace-match "" t t string) 142 | string)) 143 | 144 | (defun mood-line--string-trim-right (string) 145 | "Remove whitespace at the end of STRING." 146 | (if (string-match "[ \t\n\r]+\\'" string) 147 | (replace-match "" t t string) 148 | string)) 149 | 150 | (defun mood-line--string-trim (string) 151 | "Remove whitespace at the beginning and end of STRING." 152 | (mood-line--string-trim-left (mood-line--string-trim-right string))) 153 | 154 | (defun mood-line--format (left right) 155 | "Return a string of `window-width' length containing LEFT and RIGHT, aligned respectively." 156 | (let ((reserve (length right))) 157 | (concat left 158 | " " 159 | (propertize " " 160 | 'display `((space :align-to (- right ,reserve)))) 161 | right))) 162 | 163 | ;; 164 | ;; Update functions 165 | ;; 166 | 167 | (defvar-local mood-line--vc-text nil) 168 | (defun mood-line--update-vc-segment (&rest _) 169 | "Update `mood-line--vc-text' against the current VCS state." 170 | (setq mood-line--vc-text 171 | (when (and vc-mode buffer-file-name) 172 | (let ((backend (vc-backend buffer-file-name)) 173 | (state (vc-state buffer-file-name (vc-backend buffer-file-name)))) 174 | (let ((face 'mode-line-neutral)) 175 | (concat (cond ((memq state '(edited added)) 176 | (setq face 'mood-line-status-info) 177 | (propertize "+ " 'face face)) 178 | ((eq state 'needs-merge) 179 | (setq face 'mood-line-status-warning) 180 | (propertize "⟷ " 'face face)) 181 | ((eq state 'needs-update) 182 | (setq face 'mood-line-status-warning) 183 | (propertize "↑ " 'face face)) 184 | ((memq state '(removed conflict unregistered)) 185 | (setq face 'mood-line-status-error) 186 | (propertize "✖ " 'face face)) 187 | (t 188 | (setq face 'mood-line-status-neutral) 189 | (propertize "✔ " 'face face))) 190 | (propertize (substring vc-mode (+ (if (eq backend 'Hg) 2 3) 2)) 191 | 'face face 192 | 'mouse-face face) 193 | " ")))))) 194 | 195 | (defvar-local mood-line--flycheck-text nil) 196 | (defun mood-line--update-flycheck-segment (&optional status) 197 | "Update `mood-line--flycheck-text' against the reported flycheck STATUS." 198 | (setq mood-line--flycheck-text 199 | (pcase status 200 | ('finished (if flycheck-current-errors 201 | (let-alist (flycheck-count-errors flycheck-current-errors) 202 | (let ((sum (+ (or .error 0) (or .warning 0)))) 203 | (propertize (concat "⚑ Issues: " 204 | (number-to-string sum) 205 | " ") 206 | 'face (if .error 207 | 'mood-line-status-error 208 | 'mood-line-status-warning)))) 209 | (propertize "✔ Good " 'face 'mood-line-status-success))) 210 | ('running (propertize "Δ Checking " 'face 'mood-line-status-info)) 211 | ('errored (propertize "✖ Error " 'face 'mood-line-status-error)) 212 | ('interrupted (propertize "⏸ Paused " 'face 'mood-line-status-neutral)) 213 | ('no-checker "")))) 214 | 215 | ;; 216 | ;; Segments 217 | ;; 218 | 219 | (defun mood-line-segment-modified () 220 | "Displays a color-coded buffer modification/read-only indicator in the mode-line." 221 | (if (not (string-match-p "\\*.*\\*" (buffer-name))) 222 | (if (buffer-modified-p) 223 | (propertize "● " 'face 'mood-line-modified) 224 | (if (and buffer-read-only (buffer-file-name)) 225 | (propertize "■ " 'face 'mood-line-unimportant) 226 | " ")) 227 | " ")) 228 | 229 | (defun mood-line-segment-buffer-name () 230 | "Displays the name of the current buffer in the mode-line." 231 | (propertize "%b " 'face 'mood-line-buffer-name)) 232 | 233 | (defun mood-line-segment-anzu () 234 | "Displays color-coded anzu status information in the mode-line (if available)." 235 | (when (and (boundp 'anzu--state) anzu--state) 236 | (cond ((eq anzu--state 'replace-query) 237 | (format #("Replace: %d " 0 11 (face mood-line-status-warning)) anzu--cached-count)) 238 | (anzu--overflow-p 239 | (format #("%d/%d+ " 0 3 (face mood-line-status-info) 3 6 (face mood-line-status-error)) anzu--current-position anzu--total-matched)) 240 | (t 241 | (format #("%d/%d " 0 5 (face mood-line-status-info)) anzu--current-position anzu--total-matched))))) 242 | 243 | (defun mood-line-segment-multiple-cursors () 244 | "Displays the number of active multiple-cursors in the mode-line (if available)." 245 | (when (and (boundp 'multiple-cursors-mode) multiple-cursors-mode) 246 | (concat "MC" 247 | (format #("×%d " 0 3 (face mood-line-status-warning)) (mc/num-cursors))))) 248 | 249 | (defun mood-line-segment-position () 250 | "Displays the current cursor position in the mode-line." 251 | (concat "%l:%c" 252 | (when mood-line-show-cursor-point (propertize (format ":%d" (point)) 'face 'mood-line-unimportant)) 253 | (propertize " %p%% " 'face 'mood-line-unimportant))) 254 | 255 | (defun mood-line-segment-eol () 256 | "Displays the EOL style of the current buffer in the mode-line." 257 | (when mood-line-show-eol-style 258 | (pcase (coding-system-eol-type buffer-file-coding-system) 259 | (0 "LF ") 260 | (1 "CRLF ") 261 | (2 "CR ")))) 262 | 263 | (defun mood-line-segment-encoding () 264 | "Displays the encoding and EOL style of the buffer in the mode-line." 265 | (when mood-line-show-encoding-information 266 | (concat (let ((sys (coding-system-plist buffer-file-coding-system))) 267 | (cond ((memq (plist-get sys :category) '(coding-category-undecided coding-category-utf-8)) 268 | "UTF-8") 269 | (t (upcase (symbol-name (plist-get sys :name)))))) 270 | " "))) 271 | 272 | (defun mood-line-segment-vc () 273 | "Displays color-coded version control information in the mode-line." 274 | mood-line--vc-text) 275 | 276 | (defun mood-line-segment-major-mode () 277 | "Displays the current major mode in the mode-line." 278 | (concat (format-mode-line mode-name 'mood-line-major-mode) " ")) 279 | 280 | (defun mood-line-segment-misc-info () 281 | "Displays the current value of `mode-line-misc-info' in the mode-line." 282 | (let ((misc-info (format-mode-line mode-line-misc-info 'mood-line-unimportant))) 283 | (unless (string= (mood-line--string-trim misc-info) "") 284 | (concat (mood-line--string-trim misc-info) " ")))) 285 | 286 | (defun mood-line-segment-flycheck () 287 | "Displays color-coded flycheck information in the mode-line (if available)." 288 | mood-line--flycheck-text) 289 | 290 | (defun mood-line-segment-flymake () 291 | "Displays information about the current status of flymake in the mode-line (if available)." 292 | (when (and (boundp 'flymake-mode) flymake-mode) 293 | (concat (mood-line--string-trim (format-mode-line flymake--mode-line-format)) " "))) 294 | 295 | (defun mood-line-segment-process () 296 | "Displays the current value of `mode-line-process' in the mode-line." 297 | (let ((process-info (format-mode-line mode-line-process))) 298 | (unless (string= (mood-line--string-trim process-info) "") 299 | (concat (mood-line--string-trim process-info) " ")))) 300 | 301 | ;; 302 | ;; Activation function 303 | ;; 304 | 305 | ;; Store the default mode-line format 306 | (defvar mood-line--default-mode-line mode-line-format) 307 | 308 | ;;;###autoload 309 | (define-minor-mode mood-line-mode 310 | "Toggle mood-line on or off." 311 | :group 'mood-line 312 | :global t 313 | :lighter nil 314 | (if mood-line-mode 315 | (progn 316 | 317 | ;; Setup flycheck hooks 318 | (add-hook 'flycheck-status-changed-functions #'mood-line--update-flycheck-segment) 319 | (add-hook 'flycheck-mode-hook #'mood-line--update-flycheck-segment) 320 | 321 | ;; Setup VC hooks 322 | (add-hook 'find-file-hook #'mood-line--update-vc-segment) 323 | (add-hook 'after-save-hook #'mood-line--update-vc-segment) 324 | (advice-add #'vc-refresh-state :after #'mood-line--update-vc-segment) 325 | 326 | ;; Set the new mode-line-format 327 | (setq-default mode-line-format 328 | '((:eval 329 | (mood-line--format 330 | ;; Left 331 | (format-mode-line 332 | '(" " 333 | (:eval (mood-line-segment-modified)) 334 | (:eval (mood-line-segment-buffer-name)) 335 | (:eval (mood-line-segment-anzu)) 336 | (:eval (mood-line-segment-multiple-cursors)) 337 | (:eval (mood-line-segment-position)))) 338 | 339 | ;; Right 340 | (format-mode-line 341 | '((:eval (mood-line-segment-eol)) 342 | (:eval (mood-line-segment-encoding)) 343 | (:eval (mood-line-segment-vc)) 344 | (:eval (mood-line-segment-major-mode)) 345 | (:eval (mood-line-segment-misc-info)) 346 | (:eval (mood-line-segment-flycheck)) 347 | (:eval (mood-line-segment-flymake)) 348 | (:eval (mood-line-segment-process)) 349 | " "))))))) 350 | (progn 351 | 352 | ;; Remove flycheck hooks 353 | (remove-hook 'flycheck-status-changed-functions #'mood-line--update-flycheck-segment) 354 | (remove-hook 'flycheck-mode-hook #'mood-line--update-flycheck-segment) 355 | 356 | ;; Remove VC hooks 357 | (remove-hook 'file-find-hook #'mood-line--update-vc-segment) 358 | (remove-hook 'after-save-hook #'mood-line--update-vc-segment) 359 | (advice-remove #'vc-refresh-state #'mood-line--update-vc-segment) 360 | 361 | ;; Restore the original mode-line format 362 | (setq-default mode-line-format mood-line--default-mode-line)))) 363 | 364 | ;; 365 | ;; Provide mood-line 366 | ;; 367 | 368 | (provide 'mood-line) 369 | 370 | ;;; mood-line.el ends here 371 | --------------------------------------------------------------------------------