config

Personal configuration.
git clone git://code.dwrz.net/config
Log | Files | Refs

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