doom-modeline-core.el (60721B)
1 ;;; doom-modeline-core.el --- The core libraries for doom-modeline -*- lexical-binding: t; -*- 2 3 ;; Copyright (C) 2018-2024 Vincent Zhang 4 5 ;; This file is not part of GNU Emacs. 6 7 ;; 8 ;; This program is free software; you can redistribute it and/or modify 9 ;; it under the terms of the GNU General Public License as published by 10 ;; the Free Software Foundation, either version 3 of the License, or 11 ;; (at your option) any later version. 12 ;; 13 ;; This program is distributed in the hope that it will be useful, 14 ;; but WITHOUT ANY WARRANTY; without even the implied warranty of 15 ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 16 ;; GNU General Public License for more details. 17 ;; 18 ;; You should have received a copy of the GNU General Public License 19 ;; along with this program. If not, see <https://www.gnu.org/licenses/>. 20 ;; 21 22 ;;; Commentary: 23 ;; 24 ;; The core libraries for doom-modeline. 25 ;; 26 27 ;;; Code: 28 29 (require 'compat) 30 (eval-when-compile 31 (require 'cl-lib) 32 (require 'subr-x)) 33 (require 'nerd-icons) 34 (require 'shrink-path) 35 36 37 ;; 38 ;; Compatibility 39 ;; 40 41 (unless (boundp 'mode-line-right-align-edge) 42 (defcustom mode-line-right-align-edge 'window 43 "Where mode-line should align to. 44 Internally, that function uses `:align-to' in a display property, 45 so aligns to the left edge of the given area. See info node 46 `(elisp)Pixel Specification'. 47 48 Must be set to a symbol. Acceptable values are: 49 - `window': align to extreme right of window, regardless of margins 50 or fringes 51 - `right-fringe': align to right-fringe 52 - `right-margin': align to right-margin" 53 :type '(choice (const right-margin) 54 (const right-fringe) 55 (const window)) 56 :group 'mode-line)) 57 58 59 ;; 60 ;; Optimization 61 ;; 62 63 ;; Don’t compact font caches during GC. 64 (when (eq system-type 'windows-nt) 65 (setq inhibit-compacting-font-caches t)) 66 67 68 ;; 69 ;; Customization 70 ;; 71 72 (defgroup doom-modeline nil 73 "A minimal and modern mode-line." 74 :group 'mode-line 75 :link '(url-link :tag "Homepage" "https://github.com/seagle0128/doom-modeline")) 76 77 (defcustom doom-modeline-support-imenu nil 78 "If non-nil, cause imenu to see `doom-modeline' declarations. 79 This is done by adjusting `lisp-imenu-generic-expression' to 80 include support for finding `doom-modeline-def-*' forms. 81 82 Must be set before loading `doom-modeline'." 83 :type 'boolean 84 :set (lambda (_sym val) 85 (if val 86 (add-hook 'emacs-lisp-mode-hook #'doom-modeline-add-imenu) 87 (remove-hook 'emacs-lisp-mode-hook #'doom-modeline-add-imenu))) 88 :group 'doom-modeline) 89 90 (defcustom doom-modeline-height (+ (frame-char-height) 4) 91 "How tall the mode-line should be. It's only respected in GUI. 92 If the actual char height is larger, it respects the actual char height." 93 :type 'integer 94 :group 'doom-modeline) 95 96 (defcustom doom-modeline-bar-width 4 97 "How wide the mode-line bar should be. It's only respected in GUI." 98 :type 'integer 99 :set (lambda (sym val) 100 (set sym (if (> val 0) val 1))) 101 :group 'doom-modeline) 102 103 (defcustom doom-modeline-hud nil 104 "Whether to use hud instead of default bar. It's only respected in GUI." 105 :type 'boolean 106 :group 'doom-modeline) 107 108 (defcustom doom-modeline-hud-min-height 2 109 "Minimum height in pixels of the \"thumb\" of the hud. 110 Only respected in GUI." 111 :type 'integer 112 :set (lambda (sym val) 113 (set sym (if (> val 1) val 1))) 114 :group 'doom-modeline) 115 116 (defcustom doom-modeline-window-width-limit 85 117 "The limit of the window width. 118 119 If `window-width' is smaller than the limit, some information won't be 120 displayed. It can be an integer or a float number. nil means no limit." 121 :type '(choice integer 122 float 123 (const :tag "Disable" nil)) 124 :group 'doom-modeline) 125 126 (defcustom doom-modeline-project-detection 'auto 127 "How to detect the project root. 128 129 nil means to use `default-directory'. 130 131 The project management packages have some issues on detecting project root. 132 e.g. `projectile' doesn't handle symlink folders well, while `project' is 133 unable to handle sub-projects. 134 Specify another one if you encounter the issue." 135 :type '(choice (const :tag "Auto-detect" auto) 136 (const :tag "Find File in Project" ffip) 137 (const :tag "Projectile" projectile) 138 (const :tag "Built-in Project" project) 139 (const :tag "Disable" nil)) 140 :group 'doom-modeline) 141 142 (defcustom doom-modeline-buffer-file-name-style 'auto 143 "Determines the style used by `doom-modeline-buffer-file-name'. 144 145 Given ~/Projects/FOSS/emacs/lisp/comint.el 146 auto => emacs/l/comint.el (in a project) or comint.el 147 truncate-upto-project => ~/P/F/emacs/lisp/comint.el 148 truncate-from-project => ~/Projects/FOSS/emacs/l/comint.el 149 truncate-with-project => emacs/l/comint.el 150 truncate-except-project => ~/P/F/emacs/l/comint.el 151 truncate-upto-root => ~/P/F/e/lisp/comint.el 152 truncate-all => ~/P/F/e/l/comint.el 153 truncate-nil => ~/Projects/FOSS/emacs/lisp/comint.el 154 relative-from-project => emacs/lisp/comint.el 155 relative-to-project => lisp/comint.el 156 file-name => comint.el 157 file-name-with-project => FOSS|comint.el 158 buffer-name => comint.el<2> (uniquify buffer name)" 159 :type '(choice (const auto) 160 (const truncate-upto-project) 161 (const truncate-from-project) 162 (const truncate-with-project) 163 (const truncate-except-project) 164 (const truncate-upto-root) 165 (const truncate-all) 166 (const truncate-nil) 167 (const relative-from-project) 168 (const relative-to-project) 169 (const file-name) 170 (const file-name-with-project) 171 (const buffer-name)) 172 :group'doom-modeline) 173 174 (defcustom doom-modeline-buffer-file-true-name nil 175 "Use `file-truename' on buffer file name. 176 177 Project detection(projectile.el) may uses `file-truename' on directory path. 178 Turn on this to provide right relative path for buffer file name." 179 :type 'boolean 180 :group'doom-modeline) 181 182 (defcustom doom-modeline-icon t 183 "Whether display the icons in the mode-line. 184 185 While using the server mode in GUI, should set the value explicitly." 186 :type 'boolean 187 :group 'doom-modeline) 188 189 (defcustom doom-modeline-major-mode-icon t 190 "Whether display the icon for `major-mode'. 191 192 It respects option `doom-modeline-icon'." 193 :type 'boolean 194 :group'doom-modeline) 195 196 (defcustom doom-modeline-major-mode-color-icon t 197 "Whether display the colorful icon for `major-mode'. 198 199 It respects option `nerd-icons-color-icons'." 200 :type 'boolean 201 :group'doom-modeline) 202 203 (defcustom doom-modeline-buffer-state-icon t 204 "Whether display the icon for the buffer state. 205 206 It respects option `doom-modeline-icon'." 207 :type 'boolean 208 :group 'doom-modeline) 209 210 (defcustom doom-modeline-buffer-modification-icon t 211 "Whether display the modification icon for the buffer. 212 213 It respects option `doom-modeline-icon' and `doom-modeline-buffer-state-icon'." 214 :type 'boolean 215 :group 'doom-modeline) 216 217 (defcustom doom-modeline-lsp-icon t 218 "Whether display the icon of lsp client. 219 220 It respects option `doom-modeline-icon'." 221 :type 'boolean 222 :group 'doom-modeline) 223 224 (defcustom doom-modeline-time-icon t 225 "Whether display the icon of time. 226 227 It respects option `doom-modeline-icon'." 228 :type 'boolean 229 :group 'doom-modeline) 230 231 (defcustom doom-modeline-time-live-icon t 232 "Whether display the live icons of time. 233 234 It respects option `doom-modeline-icon' and option `doom-modeline-time-icon'." 235 :type 'boolean 236 :group 'doom-modeline) 237 238 (defcustom doom-modeline-time-analogue-clock t 239 "Whether to draw an analogue clock SVG as the live time icon. 240 It respects the option `doom-modeline-icon', option `doom-modeline-time-icon', 241 and option `doom-modeline-time-live-icon'." 242 :type 'boolean 243 :group 'doom-modeline) 244 245 (defcustom doom-modeline-time-clock-minute-resolution 1 246 "The clock will be updated every this many minutes, truncated. 247 See `doom-modeline-time-analogue-clock'." 248 :type 'natnum 249 :group 'doom-modeline) 250 251 (defcustom doom-modeline-time-clock-size 0.7 252 "Size of the analogue clock drawn, either in pixels or as a proportional height. 253 An integer value is used as the diameter of clock in pixels. 254 A floating point value sets the diameter of the clock realtive to 255 `doom-modeline-height'. 256 257 Only relevant when `doom-modeline-time-analogue-clock' is non-nil, which see." 258 :type 'number 259 :group 'doom-modeline) 260 261 (defcustom doom-modeline-unicode-fallback nil 262 "Whether to use unicode as a fallback (instead of ASCII) when not using icons." 263 :type 'boolean 264 :group 'doom-modeline) 265 266 (defcustom doom-modeline-buffer-name t 267 "Whether display the buffer name." 268 :type 'boolean 269 :group 'doom-modeline) 270 271 (defcustom doom-modeline-highlight-modified-buffer-name t 272 "Whether highlight the modified buffer name." 273 :type 'boolean 274 :group 'doom-modeline) 275 276 (defcustom doom-modeline-column-zero-based t 277 "When non-nil, mode line displays column numbers zero-based. 278 See `column-number-indicator-zero-based'." 279 :type 'boolean 280 :group 'doom-modeline) 281 282 (defcustom doom-modeline-percent-position '(-3 "%p") 283 "Specification of \"percentage offset\" of window through buffer. 284 See `mode-line-percent-position'." 285 :type '(radio 286 (const :tag "nil: No offset is displayed" nil) 287 (const :tag "\"%o\": Proportion of \"travel\" of the window through the buffer" 288 (-3 "%o")) 289 (const :tag "\"%p\": Percentage offset of top of window" 290 (-3 "%p")) 291 (const :tag "\"%P\": Percentage offset of bottom of window" 292 (-3 "%P")) 293 (const :tag "\"%q\": Offsets of both top and bottom of window" 294 (6 "%q"))) 295 :group 'doom-modeline) 296 297 (defcustom doom-modeline-position-line-format '("L%l") 298 "Format used to display line numbers in the mode line. 299 See `mode-line-position-line-format'." 300 :type '(list string) 301 :group 'doom-modeline) 302 303 (defcustom doom-modeline-position-column-format '("C%c") 304 "Format used to display column numbers in the mode line. 305 See `mode-line-position-column-format'." 306 :type '(list string) 307 :group 'doom-modeline) 308 309 (defcustom doom-modeline-position-column-line-format '("%l:%c") 310 "Format used to display combined line/column numbers in the mode line. 311 See `mode-line-position-column-line-format'." 312 :type '(list string) 313 :group 'doom-modeline) 314 315 (defcustom doom-modeline-minor-modes nil 316 "Whether display the minor modes in the mode-line." 317 :type 'boolean 318 :group 'doom-modeline) 319 320 (defcustom doom-modeline-enable-word-count nil 321 "If non-nil, a word count will be added to the selection-info modeline segment." 322 :type 'boolean 323 :group 'doom-modeline) 324 325 (defcustom doom-modeline-continuous-word-count-modes 326 '(markdown-mode gfm-mode org-mode) 327 "Major modes in which to display word count continuously. 328 329 It respects `doom-modeline-enable-word-count'." 330 :type '(repeat (symbol :tag "Major-Mode") ) 331 :group 'doom-modeline) 332 333 (defcustom doom-modeline-buffer-encoding t 334 "Whether display the buffer encoding." 335 :type '(choice (const :tag "Always" t) 336 (const :tag "When non-default" nondefault) 337 (const :tag "Never" nil)) 338 :group 'doom-modeline) 339 340 (defcustom doom-modeline-default-coding-system 'utf-8 341 "Default coding system for `doom-modeline-buffer-encoding' `nondefault'." 342 :type 'coding-system 343 :group 'doom-modeline) 344 345 (defcustom doom-modeline-default-eol-type 0 346 "Default EOL type for `doom-modeline-buffer-encoding' `nondefault'." 347 :type '(choice (const :tag "Unix-style LF" 0) 348 (const :tag "DOS-style CRLF" 1) 349 (const :tag "Mac-style CR" 2)) 350 :group 'doom-modeline) 351 352 (defcustom doom-modeline-indent-info nil 353 "Whether display the indentation information." 354 :type 'boolean 355 :group 'doom-modeline) 356 357 (defcustom doom-modeline-total-line-number nil 358 "Whether display the total line number." 359 :type 'boolean 360 :group 'doom-modeline) 361 362 ;; It is based upon `editorconfig-indentation-alist' but is used to read indentation levels instead 363 ;; of setting them. (https://github.com/editorconfig/editorconfig-emacs) 364 (defcustom doom-modeline-indent-alist 365 '((apache-mode apache-indent-level) 366 (awk-mode c-basic-offset) 367 (bpftrace-mode c-basic-offset) 368 (c++-mode c-basic-offset) 369 (c-mode c-basic-offset) 370 (cmake-mode cmake-tab-width) 371 (coffee-mode coffee-tab-width) 372 (cperl-mode cperl-indent-level) 373 (crystal-mode crystal-indent-level) 374 (csharp-mode c-basic-offset) 375 (css-mode css-indent-offset) 376 (d-mode c-basic-offset) 377 (emacs-lisp-mode lisp-indent-offset) 378 (enh-ruby-mode enh-ruby-indent-level) 379 (erlang-mode erlang-indent-level) 380 (ess-mode ess-indent-offset) 381 (f90-mode f90-associate-indent 382 f90-continuation-indent 383 f90-critical-indent 384 f90-do-indent 385 f90-if-indent 386 f90-program-indent 387 f90-type-indent) 388 (feature-mode feature-indent-offset 389 feature-indent-level) 390 (fsharp-mode fsharp-continuation-offset 391 fsharp-indent-level 392 fsharp-indent-offset) 393 (groovy-mode groovy-indent-offset) 394 (haskell-mode haskell-indent-spaces 395 haskell-indent-offset 396 haskell-indentation-layout-offset 397 haskell-indentation-left-offset 398 haskell-indentation-starter-offset 399 haskell-indentation-where-post-offset 400 haskell-indentation-where-pre-offset 401 shm-indent-spaces) 402 (haxor-mode haxor-tab-width) 403 (idl-mode c-basic-offset) 404 (jade-mode jade-tab-width) 405 (java-mode c-basic-offset) 406 (js-mode js-indent-level) 407 (js-jsx-mode js-indent-level 408 sgml-basic-offset) 409 (js2-mode js2-basic-offset) 410 (js2-jsx-mode js2-basic-offset 411 sgml-basic-offset) 412 (js3-mode js3-indent-level) 413 (json-mode js-indent-level) 414 (julia-mode julia-indent-offset) 415 (kotlin-mode kotlin-tab-width) 416 (latex-mode tex-indent-basic) 417 (lisp-mode lisp-indent-offset) 418 (livescript-mode livescript-tab-width) 419 (lua-mode lua-indent-level) 420 (matlab-mode matlab-indent-level) 421 (mips-mode mips-tab-width) 422 (mustache-mode mustache-basic-offset) 423 (nasm-mode nasm-basic-offset) 424 (nginx-mode nginx-indent-level) 425 (nxml-mode nxml-child-indent) 426 (objc-mode c-basic-offset) 427 (octave-mode octave-block-offset) 428 (perl-mode perl-indent-level) 429 (php-mode c-basic-offset) 430 (pike-mode c-basic-offset) 431 (ps-mode ps-mode-tab) 432 (pug-mode pug-tab-width) 433 (puppet-mode puppet-indent-level) 434 (python-mode python-indent-offset) 435 (ruby-mode ruby-indent-level) 436 (rust-mode rust-indent-offset) 437 (rustic-mode rustic-indent-offset) 438 (scala-mode scala-indent:step) 439 (scss-mode css-indent-offset) 440 (sgml-mode sgml-basic-offset) 441 (sh-mode sh-basic-offset 442 sh-indentation) 443 (slim-mode slim-indent-offset) 444 (sml-mode sml-indent-level) 445 (tcl-mode tcl-indent-level 446 tcl-continued-indent-level) 447 (terra-mode terra-indent-level) 448 (typescript-mode typescript-indent-level) 449 (verilog-mode verilog-indent-level 450 verilog-indent-level-behavioral 451 verilog-indent-level-declaration 452 verilog-indent-level-module 453 verilog-cexp-indent 454 verilog-case-indent) 455 (web-mode web-mode-attr-indent-offset 456 web-mode-attr-value-indent-offset 457 web-mode-code-indent-offset 458 web-mode-css-indent-offset 459 web-mode-markup-indent-offset 460 web-mode-sql-indent-offset 461 web-mode-block-padding 462 web-mode-script-padding 463 web-mode-style-padding) 464 (yaml-mode yaml-indent-offset)) 465 "Indentation retrieving variables matched to major modes. 466 467 Which is used when `doom-modeline-indent-info' is non-nil. 468 When multiple variables are specified for a mode, they will be tried resolved 469 in the given order." 470 :type '(alist :key-type symbol :value-type sexp) 471 :group 'doom-modeline) 472 473 (defcustom doom-modeline-vcs-icon t 474 "Whether display the icon of vcs segment. 475 476 It respects option `doom-modeline-icon'." 477 :type 'boolean 478 :group 'doom-modeline) 479 480 (defcustom doom-modeline-check-icon t 481 "Whether display the icon of check segment. 482 483 It respects option `doom-modeline-icon'." 484 :type 'boolean 485 :group 'doom-modeline) 486 487 (define-obsolete-variable-alias 488 'doom-modeline-checker-simple-format 489 'doom-modeline-check-simple-format 490 "4.2.0") 491 492 (defcustom doom-modeline-check-simple-format nil 493 "If non-nil, only display one number for check information if applicable." 494 :type 'boolean 495 :group 'doom-modeline) 496 497 (defcustom doom-modeline-number-limit 99 498 "The maximum number displayed for notifications." 499 :type 'integer 500 :group 'doom-modeline) 501 502 (defcustom doom-modeline-vcs-max-length 12 503 "The maximum displayed length of the branch name of version control." 504 :type 'integer 505 :group 'doom-modeline) 506 507 (defcustom doom-modeline-workspace-name t 508 "Whether display the workspace name. 509 510 Non-nil to display in the mode-line." 511 :type 'boolean 512 :group 'doom-modeline) 513 514 (defcustom doom-modeline-persp-name t 515 "Whether display the perspective name. 516 517 Non-nil to display in the mode-line." 518 :type 'boolean 519 :group 'doom-modeline) 520 521 (defcustom doom-modeline-display-default-persp-name nil 522 "If non nil the default perspective name is displayed in the mode-line." 523 :type 'boolean 524 :group 'doom-modeline) 525 526 (defcustom doom-modeline-persp-icon t 527 "If non nil the perspective name is displayed alongside a folder icon." 528 :type 'boolean 529 :group 'doom-modeline) 530 531 (defcustom doom-modeline-repl t 532 "Whether display the `repl' state. 533 534 Non-nil to display in the mode-line." 535 :type 'boolean 536 :group 'doom-modeline) 537 538 (defcustom doom-modeline-lsp t 539 "Whether display the `lsp' state. 540 541 Non-nil to display in the mode-line." 542 :type 'boolean 543 :group 'doom-modeline) 544 545 (defcustom doom-modeline-github nil 546 "Whether display the GitHub notifications. 547 548 It requires `ghub' and `async' packages. Additionally, your GitHub personal 549 access token must have `notifications' permissions. 550 551 If you use `pass' to manage your secrets, you also need to add this hook: 552 (add-hook \\='doom-modeline-before-github-fetch-notification-hook 553 #\\='auth-source-pass-enable)" 554 :type 'boolean 555 :group 'doom-modeline) 556 557 (defcustom doom-modeline-github-interval 1800 ; (* 30 60) 558 "The interval of checking GitHub." 559 :type 'integer 560 :group 'doom-modeline) 561 562 (defcustom doom-modeline-env-version t 563 "Whether display the environment version." 564 :type 'boolean 565 :group 'doom-modeline) 566 567 (defcustom doom-modeline-modal t 568 "Whether display the modal state. 569 570 Including `evil', `overwrite', `god', `ryo' and `xah-fly-keys', etc." 571 :type 'boolean 572 :group 'doom-modeline) 573 574 (defcustom doom-modeline-modal-icon t 575 "Whether display the modal state icon. 576 577 Including `evil', `overwrite', `god', `ryo' and `xah-fly-keys', etc." 578 :type 'boolean 579 :group 'doom-modeline) 580 581 (defcustom doom-modeline-modal-modern-icon t 582 "Whether display the modern icons for modals." 583 :type 'boolean 584 :group 'doom-modeline) 585 586 (defcustom doom-modeline-always-show-macro-register nil 587 "When non-nil, always show the register name when recording an evil macro." 588 :type 'boolean 589 :group 'doom-modeline) 590 591 (defcustom doom-modeline-mu4e nil 592 "Whether display the mu4e notifications. 593 594 It requires `mu4e-alert' package." 595 :type 'boolean 596 :group 'doom-modeline) 597 598 (defcustom doom-modeline-gnus nil 599 "Whether to display notifications from gnus. 600 601 It requires `gnus' to be setup" 602 :type 'boolean 603 :group 'doom-modeline) 604 605 (defcustom doom-modeline-gnus-timer 2 606 "The wait time in minutes before gnus fetches mail. 607 608 If nil, don't set up a hook." 609 :type 'integer 610 :group 'doom-modeline) 611 612 (defcustom doom-modeline-gnus-idle nil 613 "Whether to wait an idle time to scan for news. 614 615 When t, sets `doom-modeline-gnus-timer' as an idle timer. If a 616 number, Emacs must have been idle this given time, checked after 617 reach the defined timer, to fetch news. The time step can be 618 configured in `gnus-demon-timestep'." 619 :type '(choice 620 (boolean :tag "Set `doom-modeline-gnus-timer' as an idle timer") 621 (number :tag "Set a custom idle timer")) 622 :group 'doom-modeline) 623 624 (defcustom doom-modeline-gnus-excluded-groups nil 625 "A list of groups to be excluded from the unread count. 626 Groups' names list in `gnus-newsrc-alist'`" 627 :type '(repeat string) 628 :group 'doom-modeline) 629 630 (defcustom doom-modeline-irc t 631 "Whether display the irc notifications. 632 633 It requires `circe' or `erc' package." 634 :type 'boolean 635 :group 'doom-modeline) 636 637 (defcustom doom-modeline-irc-buffers nil 638 "Whether display the unread irc buffers." 639 :type 'boolean 640 :group 'doom-modeline) 641 642 (defcustom doom-modeline-irc-stylize 'identity 643 "Function to stylize the irc buffer names." 644 :type 'function 645 :group 'doom-modeline) 646 647 (defcustom doom-modeline-battery t 648 "Whether display the battery status. 649 650 It respects `display-battery-mode'." 651 :type 'boolean 652 :group 'doom-modeline) 653 654 (defcustom doom-modeline-time t 655 "Whether display the time. 656 657 It respects `display-time-mode'." 658 :type 'boolean 659 :group 'doom-modeline) 660 661 (defcustom doom-modeline-display-misc-in-all-mode-lines t 662 "Whether display the misc segment on all mode lines. 663 664 If nil, display only if the mode line is active." 665 :type 'boolean 666 :group 'doom-modeline) 667 668 (defcustom doom-modeline-always-visible-segments nil 669 "A list of segments that should be visible even in inactive windows." 670 :type '(repeat symbol) 671 :group 'doom-modeline) 672 673 (defcustom doom-modeline-buffer-file-name-function #'identity 674 "The function to handle variable `buffer-file-name'." 675 :type 'function 676 :group 'doom-modeline) 677 678 (defcustom doom-modeline-buffer-file-truename-function #'identity 679 "The function to handle `buffer-file-truename'." 680 :type 'function 681 :group 'doom-modeline) 682 683 (defcustom doom-modeline-k8s-show-namespace t 684 "Whether to show the current Kubernetes context's default namespace." 685 :type 'boolean 686 :group 'doom-modeline) 687 688 689 ;; 690 ;; Faces 691 ;; 692 693 (defgroup doom-modeline-faces nil 694 "The faces of `doom-modeline'." 695 :group 'doom-modeline 696 :group 'faces 697 :link '(url-link :tag "Homepage" "https://github.com/seagle0128/doom-modeline")) 698 699 (defface doom-modeline 700 '((t ())) 701 "Default face." 702 :group 'doom-modeline-faces) 703 704 (defface doom-modeline-emphasis 705 '((t (:inherit (doom-modeline mode-line-emphasis)))) 706 "Face used for emphasis." 707 :group 'doom-modeline-faces) 708 709 (defface doom-modeline-highlight 710 '((t (:inherit (doom-modeline mode-line-highlight)))) 711 "Face used for highlighting." 712 :group 'doom-modeline-faces) 713 714 (defface doom-modeline-buffer-path 715 '((t (:inherit (doom-modeline-emphasis bold)))) 716 "Face used for the dirname part of the buffer path." 717 :group 'doom-modeline-faces) 718 719 (defface doom-modeline-buffer-file 720 '((t (:inherit (doom-modeline mode-line-buffer-id bold)))) 721 "Face used for the filename part of the mode-line buffer path." 722 :group 'doom-modeline-faces) 723 724 (defface doom-modeline-buffer-modified 725 '((t (:inherit (doom-modeline warning bold) :background unspecified))) 726 "Face used for the \\='unsaved\\=' symbol in the mode-line." 727 :group 'doom-modeline-faces) 728 729 (defface doom-modeline-buffer-major-mode 730 '((t (:inherit (doom-modeline-emphasis bold)))) 731 "Face used for the major-mode segment in the mode-line." 732 :group 'doom-modeline-faces) 733 734 (defface doom-modeline-buffer-minor-mode 735 '((t (:inherit (doom-modeline font-lock-doc-face) :weight normal :slant normal))) 736 "Face used for the minor-modes segment in the mode-line." 737 :group 'doom-modeline-faces) 738 739 (defface doom-modeline-project-parent-dir 740 '((t (:inherit (doom-modeline font-lock-comment-face bold)))) 741 "Face used for the project parent directory of the mode-line buffer path." 742 :group 'doom-modeline-faces) 743 744 (defface doom-modeline-project-dir 745 '((t (:inherit (doom-modeline font-lock-string-face bold)))) 746 "Face used for the project directory of the mode-line buffer path." 747 :group 'doom-modeline-faces) 748 749 (defface doom-modeline-project-root-dir 750 '((t (:inherit (doom-modeline-emphasis bold)))) 751 "Face used for the project part of the mode-line buffer path." 752 :group 'doom-modeline-faces) 753 754 (defface doom-modeline-panel 755 '((t (:inherit doom-modeline-highlight))) 756 "Face for \\='X out of Y\\=' segments. 757 This applies to `anzu', `evil-substitute', `iedit' etc." 758 :group 'doom-modeline-faces) 759 760 (defface doom-modeline-host 761 '((t (:inherit (doom-modeline italic)))) 762 "Face for remote hosts in the mode-line." 763 :group 'doom-modeline-faces) 764 765 (defface doom-modeline-input-method 766 '((t (:inherit (doom-modeline-emphasis)))) 767 "Face for input method in the mode-line." 768 :group 'doom-modeline-faces) 769 770 (defface doom-modeline-input-method-alt 771 '((t (:inherit (doom-modeline font-lock-doc-face) :slant normal))) 772 "Alternative face for input method in the mode-line." 773 :group 'doom-modeline-faces) 774 775 (defface doom-modeline-debug 776 '((t (:inherit (doom-modeline font-lock-doc-face) :slant normal))) 777 "Face for debug-level messages in the mode-line. Used by vcs, check, etc." 778 :group 'doom-modeline-faces) 779 780 (defface doom-modeline-info 781 '((t (:inherit (doom-modeline success)))) 782 "Face for info-level messages in the mode-line. Used by vcs, check, etc." 783 :group 'doom-modeline-faces) 784 785 (defface doom-modeline-warning 786 '((t (:inherit (doom-modeline warning)))) 787 "Face for warnings in the mode-line. Used by vcs, check, etc." 788 :group 'doom-modeline-faces) 789 790 (defface doom-modeline-urgent 791 '((t (:inherit (doom-modeline error)))) 792 "Face for errors in the mode-line. Used by vcs, check, etc." 793 :group 'doom-modeline-faces) 794 795 (defface doom-modeline-notification 796 '((t (:inherit doom-modeline-warning))) 797 "Face for notifications in the mode-line. Used by GitHub, mu4e, etc. 798 Also see the face `doom-modeline-unread-number'." 799 :group 'doom-modeline-faces) 800 801 (defface doom-modeline-unread-number 802 '((t (:inherit doom-modeline :slant italic))) 803 "Face for unread number in the mode-line. Used by GitHub, mu4e, etc." 804 :group 'doom-modeline-faces) 805 806 (defface doom-modeline-bar 807 '((t (:inherit doom-modeline-highlight))) 808 "The face used for the left-most bar in the mode-line of an active window." 809 :group 'doom-modeline-faces) 810 811 (defface doom-modeline-bar-inactive 812 `((t (:inherit doom-modeline))) 813 "The face used for the left-most bar in the mode-line of an inactive window." 814 :group 'doom-modeline-faces) 815 816 (defface doom-modeline-debug-visual 817 '((((background light)) :foreground "#D4843E" :inherit doom-modeline) 818 (((background dark)) :foreground "#915B2D" :inherit doom-modeline)) 819 "Face to use for the mode-line while debugging." 820 :group 'doom-modeline-faces) 821 822 (defface doom-modeline-evil-emacs-state 823 '((t (:inherit (doom-modeline font-lock-builtin-face)))) 824 "Face for the Emacs state tag in evil indicator." 825 :group 'doom-modeline-faces) 826 827 (defface doom-modeline-evil-insert-state 828 '((t (:inherit (doom-modeline font-lock-keyword-face)))) 829 "Face for the insert state tag in evil indicator." 830 :group 'doom-modeline-faces) 831 832 (defface doom-modeline-evil-motion-state 833 '((t (:inherit (doom-modeline font-lock-doc-face) :slant normal))) 834 "Face for the motion state tag in evil indicator." 835 :group 'doom-modeline-faces) 836 837 (defface doom-modeline-evil-normal-state 838 '((t (:inherit doom-modeline-info))) 839 "Face for the normal state tag in evil indicator." 840 :group 'doom-modeline-faces) 841 842 (defface doom-modeline-evil-operator-state 843 '((t (:inherit (doom-modeline mode-line)))) 844 "Face for the operator state tag in evil indicator." 845 :group 'doom-modeline-faces) 846 847 (defface doom-modeline-evil-visual-state 848 '((t (:inherit doom-modeline-warning))) 849 "Face for the visual state tag in evil indicator." 850 :group 'doom-modeline-faces) 851 852 (defface doom-modeline-evil-replace-state 853 '((t (:inherit doom-modeline-urgent))) 854 "Face for the replace state tag in evil indicator." 855 :group 'doom-modeline-faces) 856 857 (defface doom-modeline-overwrite 858 '((t (:inherit doom-modeline-urgent))) 859 "Face for overwrite indicator." 860 :group 'doom-modeline-faces) 861 862 (defface doom-modeline-god 863 '((t (:inherit doom-modeline-info))) 864 "Face for god-mode indicator." 865 :group 'doom-modeline-faces) 866 867 (defface doom-modeline-ryo 868 '((t (:inherit doom-modeline-info))) 869 "Face for RYO indicator." 870 :group 'doom-modeline-faces) 871 872 (defface doom-modeline-fly-insert-state 873 '((t (:inherit (doom-modeline font-lock-keyword-face)))) 874 "Face for the insert state in xah-fly-keys indicator." 875 :group 'doom-modeline-faces) 876 877 (defface doom-modeline-fly-normal-state 878 '((t (:inherit doom-modeline-info))) 879 "Face for the normal state in xah-fly-keys indicator." 880 :group 'doom-modeline-faces) 881 882 (defface doom-modeline-boon-command-state 883 '((t (:inherit doom-modeline-info))) 884 "Face for the command state tag in boon indicator." 885 :group 'doom-modeline-faces) 886 887 (defface doom-modeline-boon-insert-state 888 '((t (:inherit (doom-modeline font-lock-keyword-face)))) 889 "Face for the insert state tag in boon indicator." 890 :group 'doom-modeline-faces) 891 892 (defface doom-modeline-boon-special-state 893 '((t (:inherit (doom-modeline font-lock-builtin-face)))) 894 "Face for the special state tag in boon indicator." 895 :group 'doom-modeline-faces) 896 897 (defface doom-modeline-boon-off-state 898 '((t (:inherit (doom-modeline mode-line)))) 899 "Face for the off state tag in boon indicator." 900 :group 'doom-modeline-faces) 901 902 (defface doom-modeline-persp-name 903 '((t (:inherit (doom-modeline font-lock-comment-face italic)))) 904 "Face for the persp name." 905 :group 'doom-modeline-faces) 906 907 (defface doom-modeline-persp-buffer-not-in-persp 908 '((t (:inherit (doom-modeline font-lock-doc-face italic)))) 909 "Face for the buffers which are not in the persp." 910 :group 'doom-modeline-faces) 911 912 (defface doom-modeline-repl-success 913 '((t (:inherit doom-modeline-info))) 914 "Face for REPL success state." 915 :group 'doom-modeline-faces) 916 917 (defface doom-modeline-repl-warning 918 '((t (:inherit doom-modeline-warning))) 919 "Face for REPL warning state." 920 :group 'doom-modeline-faces) 921 922 (defface doom-modeline-lsp-success 923 '((t (:inherit doom-modeline-info))) 924 "Face for LSP success state." 925 :group 'doom-modeline-faces) 926 927 (defface doom-modeline-lsp-warning 928 '((t (:inherit doom-modeline-warning))) 929 "Face for LSP warning state." 930 :group 'doom-modeline-faces) 931 932 (defface doom-modeline-lsp-error 933 '((t (:inherit doom-modeline-urgent))) 934 "Face for LSP error state." 935 :group 'doom-modeline-faces) 936 937 (defface doom-modeline-lsp-running 938 '((t (:inherit (doom-modeline compilation-mode-line-run) :weight normal :slant normal))) 939 "Face for LSP running state." 940 :group 'doom-modeline-faces) 941 942 (defface doom-modeline-battery-charging 943 '((t (:inherit doom-modeline-info))) 944 "Face for battery charging status." 945 :group 'doom-modeline-faces) 946 947 (defface doom-modeline-battery-full 948 '((t (:inherit doom-modeline-info))) 949 "Face for battery full status." 950 :group 'doom-modeline-faces) 951 952 (defface doom-modeline-battery-normal 953 '((t (:inherit (doom-modeline mode-line)))) 954 "Face for battery normal status." 955 :group 'doom-modeline-faces) 956 957 (defface doom-modeline-battery-warning 958 '((t (:inherit doom-modeline-warning))) 959 "Face for battery warning status." 960 :group 'doom-modeline-faces) 961 962 (defface doom-modeline-battery-critical 963 '((t (:inherit doom-modeline-urgent))) 964 "Face for battery critical status." 965 :group 'doom-modeline-faces) 966 967 (defface doom-modeline-battery-error 968 '((t (:inherit doom-modeline-urgent))) 969 "Face for battery error status." 970 :group 'doom-modeline-faces) 971 972 (defface doom-modeline-buffer-timemachine 973 '((t (:inherit doom-modeline-buffer-file :slant italic))) 974 "Face for timemachine status." 975 :group 'doom-modeline-faces) 976 977 (defface doom-modeline-time 978 '((t (:inherit doom-modeline))) 979 "Face for display time." 980 :group 'doom-modeline-faces) 981 982 (defface doom-modeline-compilation 983 '((t (:inherit doom-modeline-warning :slant italic :height 0.9))) 984 "Face for compilation progress." 985 :group 'doom-modeline-faces) 986 987 ;; 988 ;; Externals 989 ;; 990 991 (defvar mode-line-right-align-edge) 992 993 (declare-function face-remap-remove-relative "face-remap") 994 (declare-function ffip-project-root "ext:find-file-in-project") 995 (declare-function project-root "project") 996 (declare-function projectile-project-root "ext:projectile") 997 998 999 ;; 1000 ;; Utilities 1001 ;; 1002 1003 (defun doom-modeline-add-font-lock () 1004 "Fontify `doom-modeline-def-*' statements." 1005 (font-lock-add-keywords 1006 'emacs-lisp-mode 1007 '(("(\\(doom-modeline-def-.+\\)\\_> +\\(.*?\\)\\_>" 1008 (1 font-lock-keyword-face) 1009 (2 font-lock-constant-face))))) 1010 (doom-modeline-add-font-lock) 1011 1012 (defun doom-modeline-add-imenu () 1013 "Add to `imenu' index." 1014 (add-to-list 1015 'imenu-generic-expression 1016 '("Modelines" 1017 "^\\s-*(\\(doom-modeline-def-modeline\\)\\s-+\\(\\(?:\\sw\\|\\s_\\|\\s'\\|\\\\.\\)+\\)" 1018 2)) 1019 (add-to-list 1020 'imenu-generic-expression 1021 '("Segments" 1022 "^\\s-*(\\(doom-modeline-def-segment\\)\\s-+\\(\\(?:\\sw\\|\\s_\\|\\\\.\\)+\\)" 1023 2)) 1024 (add-to-list 1025 'imenu-generic-expression 1026 '("Envs" 1027 "^\\s-*(\\(doom-modeline-def-env\\)\\s-+\\(\\(?:\\sw\\|\\s_\\|\\\\.\\)+\\)" 1028 2))) 1029 1030 1031 ;; 1032 ;; Core helpers 1033 ;; 1034 1035 ;; FIXME #183: Force to calculate mode-line height 1036 ;; @see https://github.com/seagle0128/doom-modeline/issues/183 1037 ;; @see https://github.com/seagle0128/doom-modeline/issues/483 1038 (unless (>= emacs-major-version 29) 1039 (eval-and-compile 1040 (defun doom-modeline-redisplay (&rest _) 1041 "Call `redisplay' to trigger mode-line height calculations. 1042 1043 Certain functions, including e.g. `fit-window-to-buffer', base 1044 their size calculations on values which are incorrect if the 1045 mode-line has a height different from that of the `default' face 1046 and certain other calculations have not yet taken place for the 1047 window in question. 1048 1049 These calculations can be triggered by calling `redisplay' 1050 explicitly at the appropriate time and this functions purpose 1051 is to make it easier to do so. 1052 1053 This function is like `redisplay' with non-nil FORCE argument, 1054 but it will only trigger a redisplay when there is a non nil 1055 `mode-line-format' and the height of the mode-line is different 1056 from that of the `default' face. This function is intended to be 1057 used as an advice to window creation functions." 1058 (when (and (bound-and-true-p doom-modeline-mode) 1059 mode-line-format 1060 (/= (frame-char-height) (window-mode-line-height))) 1061 (redisplay t)))) 1062 (advice-add #'fit-window-to-buffer :before #'doom-modeline-redisplay)) 1063 1064 ;; For `flycheck-color-mode-line' 1065 (with-eval-after-load 'flycheck-color-mode-line 1066 (defvar flycheck-color-mode-line-face-to-color) 1067 (setq flycheck-color-mode-line-face-to-color 'doom-modeline)) 1068 1069 (defun doom-modeline-icon-displayable-p () 1070 "Return non-nil if icons are displayable." 1071 (and doom-modeline-icon (featurep 'nerd-icons))) 1072 1073 (defun doom-modeline-mwheel-available-p () 1074 "Whether mouse wheel is available." 1075 (and (featurep 'mwheel) (bound-and-true-p mouse-wheel-mode))) 1076 1077 ;; Keep `doom-modeline-current-window' up-to-date 1078 (defun doom-modeline--selected-window () 1079 "Get the selected window." 1080 (frame-selected-window)) 1081 1082 (defvar doom-modeline-current-window (doom-modeline--selected-window) 1083 "Current window.") 1084 1085 (defun doom-modeline--active () 1086 "Whether is an active window." 1087 (unless (and (bound-and-true-p mini-frame-frame) 1088 (and (frame-live-p mini-frame-frame) 1089 (frame-visible-p mini-frame-frame))) 1090 (and doom-modeline-current-window 1091 (eq (doom-modeline--selected-window) doom-modeline-current-window)))) 1092 1093 (defvar-local doom-modeline--limited-width-p nil) 1094 1095 (defun doom-modeline--segment-visible (name) 1096 "Whether the segment NAME should be displayed." 1097 (and 1098 (or (doom-modeline--active) 1099 (member name doom-modeline-always-visible-segments)) 1100 (not doom-modeline--limited-width-p))) 1101 1102 (defun doom-modeline-set-selected-window (&rest _) 1103 "Set `doom-modeline-current-window' appropriately." 1104 (let ((win (doom-modeline--selected-window))) 1105 (setq doom-modeline-current-window 1106 (if (minibuffer-window-active-p win) 1107 (minibuffer-selected-window) 1108 win)))) 1109 1110 (defun doom-modeline-unset-selected-window () 1111 "Unset `doom-modeline-current-window' appropriately." 1112 (setq doom-modeline-current-window nil)) 1113 1114 (add-hook 'pre-redisplay-functions #'doom-modeline-set-selected-window) 1115 1116 ;; Ensure modeline is inactive when Emacs is unfocused 1117 (defvar doom-modeline--remap-faces '(mode-line 1118 mode-line-active 1119 mode-line-emphasis 1120 mode-line-highlight 1121 mode-line-buffer-id 1122 doom-modeline 1123 solaire-mode-line-face 1124 solaire-mode-line-active-face 1125 paradox-mode-line-face 1126 flycheck-color-mode-line-error-face 1127 flycheck-color-mode-line-warning-face 1128 flycheck-color-mode-line-info-face 1129 flycheck-color-mode-line-success-face)) 1130 1131 (defvar doom-modeline--remap-face-cookie-alist nil) 1132 (defun doom-modeline-focus () 1133 "Focus mode-line." 1134 (mapc #'face-remap-remove-relative doom-modeline--remap-face-cookie-alist)) 1135 1136 (defun doom-modeline-unfocus () 1137 "Unfocus mode-line." 1138 (dolist (face doom-modeline--remap-faces) 1139 (add-to-list 'doom-modeline--remap-face-cookie-alist 1140 (face-remap-add-relative face 'mode-line-inactive)))) 1141 1142 (with-no-warnings 1143 (if (boundp 'after-focus-change-function) 1144 (progn 1145 (defun doom-modeline-focus-change (&rest _) 1146 (if (frame-focus-state (frame-parent)) 1147 (progn 1148 (doom-modeline-focus) 1149 ;; HACK: pulse after focusing in the frame to refresh the buffer name. 1150 ;; @see https://github.com/seagle0128/doom-modeline/issues/591 1151 (when (fboundp 'pulse-momentary-highlight-region) 1152 (pulse-momentary-highlight-region 0 0))) 1153 (doom-modeline-unfocus))) 1154 (advice-add #'handle-switch-frame :after #'doom-modeline-focus-change) 1155 (add-function :after after-focus-change-function #'doom-modeline-focus-change)) 1156 (progn 1157 (add-hook 'focus-in-hook #'doom-modeline-focus) 1158 (add-hook 'focus-out-hook #'doom-modeline-unfocus)))) 1159 1160 1161 ;; 1162 ;; Core 1163 ;; 1164 1165 (defvar doom-modeline-fn-alist ()) 1166 (defvar doom-modeline-var-alist ()) 1167 1168 (defmacro doom-modeline-def-segment (name &rest body) 1169 "Define a modeline segment NAME with BODY and byte compiles it." 1170 (declare (indent defun) (doc-string 2)) 1171 (let ((sym (intern (format "doom-modeline-segment--%s" name))) 1172 (docstring (if (stringp (car body)) 1173 (pop body) 1174 (format "%s modeline segment" name)))) 1175 (cond ((and (symbolp (car body)) 1176 (not (cdr body))) 1177 `(add-to-list 'doom-modeline-var-alist (cons ',name ',(car body)))) 1178 (t 1179 `(progn 1180 (defun ,sym () ,docstring ,@body) 1181 (add-to-list 'doom-modeline-fn-alist (cons ',name ',sym)) 1182 ,(unless (bound-and-true-p byte-compile-current-file) 1183 `(let (byte-compile-warnings) 1184 (unless (and (fboundp 'subr-native-elisp-p) 1185 (subr-native-elisp-p (symbol-function #',sym))) 1186 (byte-compile #',sym))))))))) 1187 1188 (defun doom-modeline--prepare-segments (segments) 1189 "Prepare mode-line `SEGMENTS'." 1190 (let (forms it) 1191 (dolist (seg segments) 1192 (cond ((stringp seg) 1193 (push seg forms)) 1194 ((symbolp seg) 1195 (cond ((setq it (alist-get seg doom-modeline-fn-alist)) 1196 (push (list :eval (list it)) forms)) 1197 ((setq it (alist-get seg doom-modeline-var-alist)) 1198 (push it forms)) 1199 ((error "%s is not a defined segment" seg)))) 1200 ((error "%s is not a valid segment" seg)))) 1201 (nreverse forms))) 1202 1203 (defun doom-modeline-def-modeline (name lhs &optional rhs) 1204 "Define a modeline format and byte-compiles it. 1205 NAME is a symbol to identify it (used by `doom-modeline' for retrieval). 1206 LHS and RHS are lists of symbols of modeline segments defined with 1207 `doom-modeline-def-segment'. 1208 1209 Example: 1210 (doom-modeline-def-modeline \\='minimal 1211 \\='(bar matches \" \" buffer-info) 1212 \\='(media-info major-mode)) 1213 (doom-modeline-set-modeline \\='minimal t)" 1214 (let ((sym (intern (format "doom-modeline-format--%s" name))) 1215 (lhs-forms (doom-modeline--prepare-segments lhs)) 1216 (rhs-forms (doom-modeline--prepare-segments rhs))) 1217 (defalias sym 1218 (lambda () 1219 (list lhs-forms 1220 (let* ((rhs-str (format-mode-line (cons "" rhs-forms))) 1221 (rhs-width (progn 1222 (add-face-text-property 1223 0 (length rhs-str) 'mode-line t rhs-str) 1224 (doom-modeline-string-pixel-width rhs-str)))) 1225 (propertize 1226 " " 1227 'face (doom-modeline-face) 1228 'display 1229 ;; Backport from `mode-line-right-align-edge' in 30 1230 (if (and (display-graphic-p) 1231 (not (eq mode-line-right-align-edge 'window))) 1232 `(space :align-to (- ,mode-line-right-align-edge 1233 (,rhs-width))) 1234 `(space :align-to (,(- (window-pixel-width) 1235 (window-scroll-bar-width) 1236 (window-right-divider-width) 1237 (* (or (cdr (window-margins)) 1) 1238 (frame-char-width)) 1239 (pcase mode-line-right-align-edge 1240 ('right-margin 1241 (or (cdr (window-margins)) 0)) 1242 ('right-fringe 1243 (or (cadr (window-fringes)) 0)) 1244 (_ 0)) 1245 rhs-width)))))) 1246 rhs-forms)) 1247 (concat "Modeline:\n" 1248 (format " %s\n %s" 1249 (prin1-to-string lhs) 1250 (prin1-to-string rhs)))))) 1251 (put 'doom-modeline-def-modeline 'lisp-indent-function 'defun) 1252 1253 (defun doom-modeline (key) 1254 "Return a mode-line configuration associated with KEY (a symbol). 1255 Throws an error if it doesn't exist." 1256 (let ((fn (intern-soft (format "doom-modeline-format--%s" key)))) 1257 (when (functionp fn) 1258 `(:eval (,fn))))) 1259 1260 (defun doom-modeline-set-modeline (key &optional default) 1261 "Set the modeline format. Does nothing if the modeline KEY doesn't exist. 1262 If DEFAULT is non-nil, set the default mode-line for all buffers." 1263 (when-let ((modeline (doom-modeline key))) 1264 (setf (if default 1265 (default-value 'mode-line-format) 1266 mode-line-format) 1267 (list "%e" modeline)))) 1268 1269 ;; 1270 ;; Helpers 1271 ;; 1272 1273 (defconst doom-modeline-ellipsis 1274 (if (char-displayable-p ?…) "…" "...") 1275 "Ellipsis.") 1276 1277 (defsubst doom-modeline-spc () 1278 "Whitespace." 1279 (propertize " " 'face (doom-modeline-face))) 1280 1281 (defsubst doom-modeline-wspc () 1282 "Wide Whitespace." 1283 (propertize " " 'face (doom-modeline-face))) 1284 1285 (defsubst doom-modeline-vspc () 1286 "Thin whitespace." 1287 (propertize " " 1288 'face (doom-modeline-face) 1289 'display '((space :relative-width 0.5)))) 1290 1291 (defun doom-modeline-face (&optional face inactive-face) 1292 "Display FACE in active window, and INACTIVE-FACE in inactive window. 1293 IF FACE is nil, `mode-line' face will be used. 1294 If INACTIVE-FACE is nil, `mode-line-inactive' face will be used." 1295 (if (doom-modeline--active) 1296 (or (and (facep face) `(:inherit (doom-modeline ,face))) 1297 (and (facep 'mode-line-active) '(:inherit (doom-modeline mode-line-active))) 1298 '(:inherit (doom-modeline mode-line))) 1299 (or (and (facep face) `(:inherit (doom-modeline mode-line-inactive ,face))) 1300 (and (facep inactive-face) `(:inherit (doom-modeline ,inactive-face))) 1301 '(:inherit (doom-modeline mode-line-inactive))))) 1302 1303 (defun doom-modeline-string-pixel-width (str) 1304 "Return the width of STR in pixels." 1305 (if (fboundp 'string-pixel-width) 1306 (string-pixel-width str) 1307 (* (string-width str) (window-font-width nil 'mode-line) 1308 (if (display-graphic-p) 1.05 1.0)))) 1309 1310 (defun doom-modeline--font-height () 1311 "Calculate the actual char height of the mode-line." 1312 (let ((height (face-attribute 'mode-line :height)) 1313 (char-height (window-font-height nil 'mode-line))) 1314 (round 1315 (* 1.0 (cond ((integerp height) (/ height 10)) 1316 ((floatp height) (* height char-height)) 1317 (t char-height)))))) 1318 1319 (defun doom-modeline--original-value (sym) 1320 "Return the original value for SYM, if any. 1321 1322 If SYM has an original value, return it in a list. Return nil 1323 otherwise." 1324 (let* ((orig-val-expr (get sym 'standard-value))) 1325 (when (consp orig-val-expr) 1326 (ignore-errors 1327 (list 1328 (eval (car orig-val-expr))))))) 1329 1330 (defun doom-modeline-add-variable-watcher (symbol watch-function) 1331 "Cause WATCH-FUNCTION to be called when SYMBOL is set if possible. 1332 1333 See docs of `add-variable-watcher'." 1334 (when (fboundp 'add-variable-watcher) 1335 (add-variable-watcher symbol watch-function))) 1336 1337 (defun doom-modeline-propertize-icon (icon &optional face) 1338 "Propertize the ICON with the specified FACE. 1339 1340 The face should be the first attribute, or the font family may be overridden. 1341 So convert the face \":family XXX :height XXX :inherit XXX\" to 1342 \":inherit XXX :family XXX :height XXX\". 1343 See https://github.com/seagle0128/doom-modeline/issues/301." 1344 (when icon 1345 (if (doom-modeline-icon-displayable-p) 1346 (when-let ((props (get-text-property 0 'face icon))) 1347 (when (listp props) 1348 (cl-destructuring-bind (&key family height inherit &allow-other-keys) props 1349 (propertize icon 'face `(:inherit (doom-modeline ,(or face inherit props)) 1350 :family ,(or family "") 1351 :height ,(or height 1.0)))))) 1352 (propertize icon 'face `(:inherit (doom-modeline ,face)))))) 1353 1354 (defun doom-modeline-icon (icon-set icon-name unicode text &rest args) 1355 "Display icon of ICON-NAME with ARGS in mode-line. 1356 1357 ICON-SET includes `ipsicon', `octicon', `pomicon', `powerline', `faicon', 1358 `wicon', `sucicon', `devicon', `codicon', `flicon' and `mdicon', etc. 1359 UNICODE is the unicode char fallback. TEXT is the ASCII char fallback. 1360 ARGS is same as `nerd-icons-octicon' and others." 1361 (let ((face `(:inherit (doom-modeline 1362 ,(or (plist-get args :face) 'mode-line))))) 1363 (cond 1364 ;; Icon 1365 ((and (doom-modeline-icon-displayable-p) 1366 icon-name 1367 (not (string-empty-p icon-name))) 1368 (if-let* ((func (nerd-icons--function-name icon-set)) 1369 (icon (and (fboundp func) 1370 (apply func icon-name args)))) 1371 (doom-modeline-propertize-icon icon face) 1372 "")) 1373 ;; Unicode fallback 1374 ((and doom-modeline-unicode-fallback 1375 unicode 1376 (not (string-empty-p unicode)) 1377 (char-displayable-p (string-to-char unicode))) 1378 (propertize unicode 'face face)) 1379 ;; ASCII text 1380 (text 1381 (propertize text 'face face)) 1382 ;; Fallback 1383 (t "")))) 1384 1385 (defun doom-modeline-icon-for-buffer () 1386 "Get the formatted icon for the current buffer." 1387 (nerd-icons-icon-for-buffer)) 1388 1389 (defun doom-modeline-display-icon (icon) 1390 "Display ICON in mode-line." 1391 (if (doom-modeline--active) 1392 icon 1393 (doom-modeline-propertize-icon icon 'mode-line-inactive))) 1394 1395 (defun doom-modeline-display-text (text) 1396 "Display TEXT in mode-line." 1397 (if (doom-modeline--active) 1398 text 1399 (propertize text 'face `(:inherit (mode-line-inactive 1400 ,(get-text-property 0 'face text)))))) 1401 1402 (defun doom-modeline--create-bar-image (face width height) 1403 "Create the bar image. 1404 1405 Use FACE for the bar, WIDTH and HEIGHT are the image size in pixels." 1406 (when (and (image-type-available-p 'pbm) 1407 (numberp width) (> width 0) 1408 (numberp height) (> height 0)) 1409 (propertize 1410 " " 'display 1411 (let ((color (or (face-background face nil t) "None"))) 1412 (ignore-errors 1413 (create-image 1414 (concat (format "P1\n%i %i\n" width height) 1415 (make-string (* width height) ?1) 1416 "\n") 1417 'pbm t :scale 1 :foreground color :ascent 'center)))))) 1418 1419 (defun doom-modeline--create-hud-image 1420 (face1 face2 width height top-margin bottom-margin) 1421 "Create the hud image. 1422 1423 Use FACE1 for the bar, FACE2 for the background. 1424 WIDTH and HEIGHT are the image size in pixels. 1425 TOP-MARGIN and BOTTOM-MARGIN are the size of the margin above and below the bar, 1426 respectively." 1427 (when (and (display-graphic-p) 1428 (image-type-available-p 'pbm) 1429 (numberp width) (> width 0) 1430 (numberp height) (> height 0)) 1431 (let ((min-height (min height doom-modeline-hud-min-height))) 1432 (unless (> (- height top-margin bottom-margin) min-height) 1433 (let ((margin (- height min-height))) 1434 (setq top-margin (/ (* margin top-margin) (+ top-margin bottom-margin)) 1435 bottom-margin (- margin top-margin))))) 1436 (propertize 1437 " " 'display 1438 (let ((color1 (or (face-background face1 nil t) "None")) 1439 (color2 (or (face-background face2 nil t) "None"))) 1440 (create-image 1441 (concat 1442 (format "P1\n%i %i\n" width height) 1443 (make-string (* top-margin width) ?0) 1444 (make-string (* (- height top-margin bottom-margin) width) ?1) 1445 (make-string (* bottom-margin width) ?0) 1446 "\n") 1447 'pbm t :foreground color1 :background color2 :ascent 'center))))) 1448 1449 ;; Check whether `window-total-width' is smaller than the limit 1450 (defun doom-modeline-window-size-change-function (&rest _) 1451 "Function for `window-size-change-functions'." 1452 (setq doom-modeline--limited-width-p 1453 (cond 1454 ((integerp doom-modeline-window-width-limit) 1455 (<= (window-total-width) doom-modeline-window-width-limit)) 1456 ((floatp doom-modeline-window-width-limit) 1457 (<= (/ (window-total-width) (frame-width) 1.0) 1458 doom-modeline-window-width-limit))))) 1459 1460 (add-hook 'after-revert-hook #'doom-modeline-window-size-change-function) 1461 (add-hook 'buffer-list-update-hook #'doom-modeline-window-size-change-function) 1462 (add-hook 'window-size-change-functions #'doom-modeline-window-size-change-function) 1463 1464 (defvar-local doom-modeline--project-root nil) 1465 (defun doom-modeline--project-root () 1466 "Get the path to the project root. 1467 Return nil if no project was found." 1468 (or doom-modeline--project-root 1469 (setq doom-modeline--project-root 1470 (cond 1471 ((and (memq doom-modeline-project-detection '(auto ffip)) 1472 (fboundp 'ffip-project-root)) 1473 (let ((inhibit-message t)) 1474 (ffip-project-root))) 1475 ((and (memq doom-modeline-project-detection '(auto projectile)) 1476 (bound-and-true-p projectile-mode)) 1477 (projectile-project-root)) 1478 ((and (memq doom-modeline-project-detection '(auto project)) 1479 (fboundp 'project-current)) 1480 (when-let ((project (project-current))) 1481 (expand-file-name 1482 (if (fboundp 'project-root) 1483 (project-root project) 1484 (car (with-no-warnings 1485 (project-roots project))))))))))) 1486 1487 (doom-modeline-add-variable-watcher 1488 'doom-modeline-project-detection 1489 (lambda (_sym val op _where) 1490 (when (eq op 'set) 1491 (setq doom-modeline-project-detection val) 1492 (dolist (buf (buffer-list)) 1493 (with-current-buffer buf 1494 (setq doom-modeline--project-root nil) 1495 (and buffer-file-name (revert-buffer t t))))))) 1496 1497 (defun doom-modeline-project-p () 1498 "Check if the file is in a project." 1499 (doom-modeline--project-root)) 1500 1501 (defun doom-modeline-project-root () 1502 "Get the path to the root of your project. 1503 Return `default-directory' if no project was found." 1504 (abbreviate-file-name 1505 (or (doom-modeline--project-root) default-directory))) 1506 1507 (defun doom-modeline--format-buffer-file-name () 1508 "Get and format the buffer file name." 1509 (let ((buffer-file-name (file-local-name 1510 (or (buffer-file-name (buffer-base-buffer)) "")))) 1511 (or (and doom-modeline-buffer-file-name-function 1512 (funcall doom-modeline-buffer-file-name-function buffer-file-name)) 1513 buffer-file-name))) 1514 1515 (defun doom-modeline--format-buffer-file-truename (b-f-n) 1516 "Get and format buffer file truename via B-F-N." 1517 (let ((buffer-file-truename (file-local-name 1518 (or (file-truename b-f-n) "")))) 1519 (or (and doom-modeline-buffer-file-truename-function 1520 (funcall doom-modeline-buffer-file-truename-function buffer-file-truename)) 1521 buffer-file-truename))) 1522 1523 (defun doom-modeline-buffer-file-name () 1524 "Propertize file name based on `doom-modeline-buffer-file-name-style'." 1525 (let* ((buffer-file-name (doom-modeline--format-buffer-file-name)) 1526 (buffer-file-truename (doom-modeline--format-buffer-file-truename buffer-file-name)) 1527 (file-name 1528 (pcase doom-modeline-buffer-file-name-style 1529 ('auto 1530 (if (doom-modeline-project-p) 1531 (doom-modeline--buffer-file-name buffer-file-name buffer-file-truename 'shrink 'shrink 'hide) 1532 (propertize "%b" 'face 'doom-modeline-buffer-file))) 1533 ('truncate-upto-project 1534 (doom-modeline--buffer-file-name buffer-file-name buffer-file-truename 'shrink)) 1535 ('truncate-from-project 1536 (doom-modeline--buffer-file-name buffer-file-name buffer-file-truename nil 'shrink)) 1537 ('truncate-with-project 1538 (doom-modeline--buffer-file-name buffer-file-name buffer-file-truename 'shrink 'shrink 'hide)) 1539 ('truncate-except-project 1540 (doom-modeline--buffer-file-name buffer-file-name buffer-file-truename 'shrink 'shrink)) 1541 ('truncate-upto-root 1542 (doom-modeline--buffer-file-name-truncate buffer-file-name buffer-file-truename)) 1543 ('truncate-all 1544 (doom-modeline--buffer-file-name-truncate buffer-file-name buffer-file-truename t)) 1545 ('truncate-nil 1546 (doom-modeline--buffer-file-name buffer-file-name buffer-file-truename)) 1547 ('relative-to-project 1548 (doom-modeline--buffer-file-name-relative buffer-file-name buffer-file-truename)) 1549 ('relative-from-project 1550 (doom-modeline--buffer-file-name buffer-file-name buffer-file-truename nil nil 'hide)) 1551 ('file-name 1552 (propertize (file-name-nondirectory buffer-file-name) 1553 'face 'doom-modeline-buffer-file)) 1554 ('file-name-with-project 1555 (format "%s|%s" 1556 (propertize (file-name-nondirectory 1557 (directory-file-name (file-local-name (doom-modeline-project-root)))) 1558 'face 'doom-modeline-project-dir) 1559 (propertize (file-name-nondirectory buffer-file-name) 1560 'face 'doom-modeline-buffer-file))) 1561 ((or 'buffer-name _) 1562 (propertize "%b" 'face 'doom-modeline-buffer-file))))) 1563 (propertize (if (string-empty-p file-name) 1564 (propertize "%b" 'face 'doom-modeline-buffer-file) 1565 file-name) 1566 'mouse-face 'mode-line-highlight 1567 'help-echo (concat buffer-file-truename 1568 (unless (string= (file-name-nondirectory buffer-file-truename) 1569 (buffer-name)) 1570 (concat "\n" (buffer-name))) 1571 "\nmouse-1: Previous buffer\nmouse-3: Next buffer") 1572 'local-map mode-line-buffer-identification-keymap))) 1573 1574 (defun doom-modeline--buffer-file-name-truncate (file-path true-file-path &optional truncate-tail) 1575 "Propertize file name that truncates every dir along path. 1576 1577 If TRUNCATE-TAIL is t also truncate the parent directory of the file." 1578 (let ((dirs (shrink-path-prompt (file-name-directory true-file-path)))) 1579 (if (null dirs) 1580 (propertize "%b" 'face 'doom-modeline-buffer-file) 1581 (let ((dirname (car dirs)) 1582 (basename (cdr dirs))) 1583 (concat (propertize (concat dirname 1584 (if truncate-tail (substring basename 0 1) basename) 1585 "/") 1586 'face 'doom-modeline-project-root-dir) 1587 (propertize (file-name-nondirectory file-path) 1588 'face 'doom-modeline-buffer-file)))))) 1589 1590 (defun doom-modeline--buffer-file-name-relative (_file-path true-file-path &optional include-project) 1591 "Propertize file name showing directories relative to project's root only. 1592 1593 If INCLUDE-PROJECT is non-nil, the project path will be included." 1594 (let ((root (file-local-name (doom-modeline-project-root)))) 1595 (if (null root) 1596 (propertize "%b" 'face 'doom-modeline-buffer-file) 1597 (let ((relative-dirs (file-relative-name (file-name-directory true-file-path) 1598 (if include-project (concat root "../") root)))) 1599 (and (equal "./" relative-dirs) (setq relative-dirs "")) 1600 (concat (propertize relative-dirs 'face 'doom-modeline-buffer-path) 1601 (propertize (file-name-nondirectory true-file-path) 1602 'face 'doom-modeline-buffer-file)))))) 1603 1604 (defun doom-modeline--buffer-file-name (file-path 1605 true-file-path 1606 &optional 1607 truncate-project-root-parent 1608 truncate-project-relative-path 1609 hide-project-root-parent) 1610 "Propertize buffer name given by FILE-PATH or TRUE-FILE-PATH. 1611 1612 If TRUNCATE-PROJECT-ROOT-PARENT is non-nil will be saved by truncating project 1613 root parent down fish-shell style. 1614 1615 Example: 1616 ~/Projects/FOSS/emacs/lisp/comint.el => ~/P/F/emacs/lisp/comint.el 1617 1618 If TRUNCATE-PROJECT-RELATIVE-PATH is non-nil will be saved by truncating project 1619 relative path down fish-shell style. 1620 1621 Example: 1622 ~/Projects/FOSS/emacs/lisp/comint.el => ~/Projects/FOSS/emacs/l/comint.el 1623 1624 If HIDE-PROJECT-ROOT-PARENT is non-nil will hide project root parent. 1625 1626 Example: 1627 ~/Projects/FOSS/emacs/lisp/comint.el => emacs/lisp/comint.el" 1628 (let ((project-root (file-local-name (doom-modeline-project-root)))) 1629 (concat 1630 ;; Project root parent 1631 (unless hide-project-root-parent 1632 (when-let (root-path-parent 1633 (file-name-directory (directory-file-name project-root))) 1634 (propertize 1635 (if (and truncate-project-root-parent 1636 (not (string-empty-p root-path-parent)) 1637 (not (string= root-path-parent "/"))) 1638 (shrink-path--dirs-internal root-path-parent t) 1639 (abbreviate-file-name root-path-parent)) 1640 'face 'doom-modeline-project-parent-dir))) 1641 ;; Project directory 1642 (propertize 1643 (concat (file-name-nondirectory (directory-file-name project-root)) "/") 1644 'face 'doom-modeline-project-dir) 1645 ;; relative path 1646 (propertize 1647 (when-let (relative-path (file-relative-name 1648 (or (file-name-directory 1649 (if doom-modeline-buffer-file-true-name 1650 true-file-path file-path)) 1651 "./") 1652 project-root)) 1653 (if (string= relative-path "./") 1654 "" 1655 (if truncate-project-relative-path 1656 (substring (shrink-path--dirs-internal relative-path t) 1) 1657 relative-path))) 1658 'face 'doom-modeline-buffer-path) 1659 ;; File name 1660 (propertize (file-name-nondirectory file-path) 1661 'face 'doom-modeline-buffer-file)))) 1662 1663 (provide 'doom-modeline-core) 1664 1665 ;;; doom-modeline-core.el ends here