Merge pull request #5 from seagle0128/develop

Merge from develop branch.
This commit is contained in:
Vincent Zhang 2018-07-05 14:34:06 +08:00 committed by GitHub
commit 68c0c01bb6
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23

View file

@ -4,8 +4,8 @@
;; Author: Vincent Zhang <seagle0128@gmail.com> ;; Author: Vincent Zhang <seagle0128@gmail.com>
;; URL: https://github.com/seagle0128/doom-modeline ;; URL: https://github.com/seagle0128/doom-modeline
;; Version: 0.1.1 ;; Version: 0.2.0
;; Package-Requires: ((emacs "24.4") (all-the-icons "1.0.0") (projectile "0.10.0") (shrink-path "0.2.0") (eldoc-eval "0.1") (dash "2.11.0")) ;; Package-Requires: ((emacs "25.1") (all-the-icons "1.0.0") (projectile "0.10.0") (shrink-path "0.2.0") (eldoc-eval "0.1") (dash "2.11.0"))
;; Keywords: faces ;; Keywords: faces
;; This file is not part of GNU Emacs. ;; This file is not part of GNU Emacs.
@ -50,28 +50,25 @@
;; :ensure t ;; :ensure t
;; :defer t ;; :defer t
;; :hook (after-init . doom-modeline-init)) ;; :hook (after-init . doom-modeline-init))
;;
;;; Code: ;;; Code:
(require 'projectile)
(require 'all-the-icons) (require 'all-the-icons)
(require 'dash)
(require 'shrink-path)
(require 'eldoc-eval) (require 'eldoc-eval)
(require 'projectile)
(require 'shrink-path)
;; ;;
;; Variables ;; Variables
;; ;;
(defvar doom-modeline-height 29 (defvar doom-modeline-height 29
"How tall the mode-line should be (only respected in GUI).") "How tall the mode-line should be (only respected in GUI Emacs).")
(defvar doom-modeline-bar-width 3 (defvar doom-modeline-bar-width 3
"How wide the mode-line bar should be (only respected in GUI).") "How wide the mode-line bar should be (only respected in GUI Emacs).")
(defvar doom-modeline-vspc
(propertize " " 'face 'variable-pitch)
"Text style with icons in mode-line.")
(defvar doom-modeline-buffer-file-name-style 'truncate-upto-project (defvar doom-modeline-buffer-file-name-style 'truncate-upto-project
"Determines the style used by `doom-modeline-buffer-file-name'. "Determines the style used by `doom-modeline-buffer-file-name'.
@ -104,6 +101,68 @@ Given ~/Projects/FOSS/emacs/lisp/comint.el
(defvar iedit-mode) (defvar iedit-mode)
(defvar iedit-occurrences-overlays) (defvar iedit-occurrences-overlays)
(defvar text-scale-mode-amount) (defvar text-scale-mode-amount)
(defvar winum-auto-setup-mode-line)
;;
;; Custom faces
;;
(defgroup doom-modeline nil
"Doom mode-line faces."
:group 'faces)
(defface doom-modeline-buffer-path
'((t (:inherit (mode-line-emphasis bold))))
"Face used for the dirname part of the buffer path.")
(defface doom-modeline-buffer-file
'((t (:inherit (mode-line-buffer-id bold))))
"Face used for the filename part of the mode-line buffer path.")
(defface doom-modeline-buffer-modified
'((t (:inherit (error bold) :background nil)))
"Face used for the 'unsaved' symbol in the mode-line.")
(defface doom-modeline-buffer-major-mode
'((t (:inherit (mode-line-emphasis bold))))
"Face used for the major-mode segment in the mode-line.")
(defface doom-modeline-highlight
'((t (:inherit mode-line-emphasis)))
"Face for bright segments of the mode-line.")
(defface doom-modeline-panel
'((t (:inherit mode-line-highlight)))
"Face for 'X out of Y' segments, such as `doom-modeline--anzu', `doom-modeline--evil-substitute' and
`iedit'")
(defface doom-modeline-info
`((t (:inherit (success bold))))
"Face for info-level messages in the modeline. Used by `*vc'.")
(defface doom-modeline-warning
`((t (:inherit (warning bold))))
"Face for warnings in the modeline. Used by `*flycheck'")
(defface doom-modeline-urgent
`((t (:inherit (error bold))))
"Face for errors in the modeline. Used by `*flycheck'")
;; Bar
(defface doom-modeline-bar '((t (:inherit highlight)))
"The face used for the left-most bar on the mode-line of an active window.")
(defface doom-modeline-eldoc-bar '((t (:inherit shadow)))
"The face used for the left-most bar on the mode-line when eldoc-eval is
active.")
(defface doom-modeline-inactive-bar '((t (:inherit warning :inverse-video t)))
"The face used for the left-most bar on the mode-line of an inactive window.")
(defface doom-modeline-eyebrowse '((t ()))
"The face used for eyebrowse.")
;; ;;
;; Custom faces ;; Custom faces
@ -171,7 +230,7 @@ active.")
"The face used for brackets around the project.") "The face used for brackets around the project.")
(eval-and-compile (eval-and-compile
(defun doom-modeline--resolve-hooks (hooks) (defun doom-modeline--resolve-hook-forms (hooks)
(cl-loop with quoted-p = (eq (car-safe hooks) 'quote) (cl-loop with quoted-p = (eq (car-safe hooks) 'quote)
for hook in (doom-modeline-enlist (doom-modeline-unquote hooks)) for hook in (doom-modeline-enlist (doom-modeline-unquote hooks))
if (eq (car-safe hook) 'quote) if (eq (car-safe hook) 'quote)
@ -192,30 +251,56 @@ active.")
(defvar doom-modeline--transient-counter 0)) (defvar doom-modeline--transient-counter 0))
(defmacro doom-modeline-def-segment (name &rest forms)
"Define a modeline segment NAME by FORMS and byte compiles it." ;;
;; Modeline library
;;
(eval-and-compile
(defvar doom-modeline-fn-alist ())
(defvar doom-modeline-var-alist ()))
(defmacro doom-modeline-def-segment (name &rest body)
"Defines a modeline segment NAME with BODY and byte compiles it."
(declare (indent defun) (doc-string 2)) (declare (indent defun) (doc-string 2))
(let ((sym (intern (format "doom-modeline-segment--%s" name)))) (let ((sym (intern (format "doom-modeline-segment--%s" name)))
(docstring (if (stringp (car body))
(pop body)
(format "%s modeline segment" name))))
(cond ((and (symbolp (car body))
(not (cdr body)))
(add-to-list 'doom-modeline-var-alist (cons name (car body)))
`(add-to-list 'doom-modeline-var-alist (cons ',name ',(car body))))
(t
(add-to-list 'doom-modeline-fn-alist (cons name sym))
`(progn `(progn
(defun ,sym () ,@forms) (fset ',sym (lambda () ,docstring ,@body))
(add-to-list 'doom-modeline-fn-alist (cons ',name ',sym))
,(unless (bound-and-true-p byte-compile-current-file) ,(unless (bound-and-true-p byte-compile-current-file)
`(let (byte-compile-warnings) `(let (byte-compile-warnings)
(byte-compile #',sym)))))) (byte-compile #',sym))))))))
(defsubst doom-modeline--prepare-segments (segments) (defsubst doom-modeline--prepare-segments (segments)
"Prepare modeline segments `SEGMENTS'." "Prepare mode-line `SEGMENTS'."
(cl-loop for seg in segments (let (forms it)
if (stringp seg) (dolist (seg segments)
collect seg (cond ((stringp seg)
else (push seg forms))
collect (list (intern (format "doom-modeline-segment--%s" (symbol-name seg)))))) ((symbolp seg)
(cond ((setq it (cdr (assq seg doom-modeline-fn-alist)))
(push (list it) forms))
((setq it (cdr (assq seg doom-modeline-var-alist)))
(push it forms))
((error "%s is not a defined segment" seg))))
((error "%s is not a valid segment" seg))))
(nreverse forms)))
(defmacro doom-modeline-def-modeline (name lhs &optional rhs) (defmacro doom-modeline-def-modeline (name lhs &optional rhs)
"Define a modeline format and byte-compiles it. "Defines a modeline format and byte-compiles it.
NAME is a symbol to identify it (used by `doom-modeline' for retrieval). NAME is a symbol to identify it (used by `doom-modeline' for retrieval).
LHS and RHS are lists of symbols of modeline segments defined LHS and RHS are lists of symbols of modeline segments defined with
with `doom-modeline-def-segment'. `doom-modeline-def-segment'.
Example: Example:
(doom-modeline-def-modeline minimal (doom-modeline-def-modeline minimal
(bar matches \" \" buffer-info) (bar matches \" \" buffer-info)
@ -225,7 +310,12 @@ Example:
(lhs-forms (doom-modeline--prepare-segments lhs)) (lhs-forms (doom-modeline--prepare-segments lhs))
(rhs-forms (doom-modeline--prepare-segments rhs))) (rhs-forms (doom-modeline--prepare-segments rhs)))
`(progn `(progn
(defun ,sym () (fset ',sym
(lambda ()
,(concat "Modeline:\n"
(format " %s\n %s"
(prin1-to-string lhs)
(prin1-to-string rhs)))
(let ((lhs (list ,@lhs-forms)) (let ((lhs (list ,@lhs-forms))
(rhs (list ,@rhs-forms))) (rhs (list ,@rhs-forms)))
(let ((rhs-str (format-mode-line rhs))) (let ((rhs-str (format-mode-line rhs)))
@ -234,13 +324,15 @@ Example:
" " 'display " " 'display
`((space :align-to (- (+ right right-fringe right-margin) `((space :align-to (- (+ right right-fringe right-margin)
,(+ 1 (string-width rhs-str)))))) ,(+ 1 (string-width rhs-str))))))
rhs-str)))) rhs-str)))))
,(unless (bound-and-true-p byte-compile-current-file) ,(unless (bound-and-true-p byte-compile-current-file)
`(let (byte-compile-warnings) `(let (byte-compile-warnings)
(byte-compile #',sym)))))) (byte-compile #',sym))))))
(defun doom-modeline (key) (defun doom-modeline (key)
"Return a mode-line configuration associated with KEY (a symbol). Throws an error if it doesn't exist." "Return a mode-line configuration associated with KEY (a symbol).
Throws an error if it doesn't exist."
(let ((fn (intern (format "doom-modeline-format--%s" key)))) (let ((fn (intern (format "doom-modeline-format--%s" key))))
(when (functionp fn) (when (functionp fn)
`(:eval (,fn))))) `(:eval (,fn)))))
@ -253,7 +345,7 @@ If DEFAULT is non-nil, set the default mode-line for all buffers."
(setf (if default (setf (if default
(default-value 'mode-line-format) (default-value 'mode-line-format)
(buffer-local-value 'mode-line-format (current-buffer))) (buffer-local-value 'mode-line-format (current-buffer)))
modeline))) (list "%e" modeline))))
(defun doom-modeline-project-root () (defun doom-modeline-project-root ()
"Get the path to the root of your project. "Get the path to the root of your project.
@ -269,10 +361,9 @@ If STRICT-P, return nil if no project was found, otherwise return
;; ;;
(defun doom-modeline-eldoc (text) (defun doom-modeline-eldoc (text)
"Display string TEXT in the mode-line." "Get eldoc TEXT for mode-line."
(concat (when (display-graphic-p) (concat (when (display-graphic-p)
(doom-modeline--make-xpm (doom-modeline--make-xpm 'doom-modeline-eldoc-bar
(face-background 'doom-modeline-eldoc-bar nil t)
doom-modeline-height doom-modeline-height
doom-modeline-bar-width)) doom-modeline-bar-width))
text)) text))
@ -299,6 +390,14 @@ If STRICT-P, return nil if no project was found, otherwise return
anzu-minimum-input-length 1 anzu-minimum-input-length 1
anzu-search-threshold 250) anzu-search-threshold 250)
(defun doom-modeline-fix-anzu-count (positions here)
(cl-loop for (start . end) in positions
collect t into before
when (and (>= here start) (<= here end))
return (length before)
finally return 0))
(advice-add #'anzu--where-is-here :override #'doom-modeline-fix-anzu-count)
;; Avoid anzu conflicts across buffers ;; Avoid anzu conflicts across buffers
(mapc #'make-variable-buffer-local (mapc #'make-variable-buffer-local
'(anzu--total-matched anzu--current-position anzu--state '(anzu--total-matched anzu--current-position anzu--state
@ -317,12 +416,29 @@ If STRICT-P, return nil if no project was found, otherwise return
"Set `doom-modeline-current-window' appropriately." "Set `doom-modeline-current-window' appropriately."
(let ((win (frame-selected-window))) (let ((win (frame-selected-window)))
(unless (minibuffer-window-active-p win) (unless (minibuffer-window-active-p win)
(setq doom-modeline-current-window win)))) (setq doom-modeline-current-window win)
(force-mode-line-update))))
(defun doom-modeline-unset-selected-window ()
"Unset `doom-modeline-current-window' appropriately."
(setq doom-modeline-current-window nil)
(force-mode-line-update))
(add-hook 'window-configuration-change-hook #'doom-modeline-set-selected-window) (add-hook 'window-configuration-change-hook #'doom-modeline-set-selected-window)
(add-hook 'focus-in-hook #'doom-modeline-set-selected-window)
(advice-add #'handle-switch-frame :after #'doom-modeline-set-selected-window) (advice-add #'handle-switch-frame :after #'doom-modeline-set-selected-window)
(advice-add #'select-window :after #'doom-modeline-set-selected-window) (advice-add #'select-window :after #'doom-modeline-set-selected-window)
(with-no-warnings
(if (not (boundp 'after-focus-change-function))
(progn
(add-hook 'focus-in-hook #'doom-modeline-set-selected-window)
(add-hook 'focus-out-hook #'doom-modeline-unset-selected-window))
(defun doom-modeline-refresh-frame ()
(setq doom-modeline-current-window nil)
(cl-loop for frame in (frame-list)
if (eq (frame-focus-state frame) t)
return (setq doom-modeline-current-window (frame-selected-window frame)))
(force-mode-line-update))
(add-function :after after-focus-change-function #'doom-modeline-refresh-frame)))
;; Show version string for multi-version managers like rvm, rbenv, pyenv, etc. ;; Show version string for multi-version managers like rvm, rbenv, pyenv, etc.
(defvar-local doom-modeline-env-version nil) (defvar-local doom-modeline-env-version nil)
@ -330,7 +446,7 @@ If STRICT-P, return nil if no project was found, otherwise return
(add-hook 'focus-in-hook #'doom-modeline-update-env) (add-hook 'focus-in-hook #'doom-modeline-update-env)
(add-hook 'find-file-hook #'doom-modeline-update-env) (add-hook 'find-file-hook #'doom-modeline-update-env)
(defun doom-modeline-update-env () (defun doom-modeline-update-env ()
"Update environment for mode-line." "Update environment info on mode-line."
(when doom-modeline-env-command (when doom-modeline-env-command
(let* ((default-directory (doom-modeline-project-root)) (let* ((default-directory (doom-modeline-project-root))
(s (shell-command-to-string doom-modeline-env-command))) (s (shell-command-to-string doom-modeline-env-command)))
@ -338,21 +454,37 @@ If STRICT-P, return nil if no project was found, otherwise return
(replace-match "" t t s) (replace-match "" t t s)
s))))) s)))))
;; ;;
;; Modeline helpers ;; Modeline helpers
;; ;;
(defun doom-modeline-maybe-icon-octicon (&rest args)
"Display octicon via `ARGS'."
(when (display-graphic-p)
(apply 'all-the-icons-octicon args)))
(defun doom-modeline-maybe-icon-faicon (&rest args)
"Display font awesome icon via `ARGS'."
(when (display-graphic-p)
(apply 'all-the-icons-faicon args)))
(defun doom-modeline-maybe-icon-material (&rest args)
"Display material icon via `ARGS'."
(when (display-graphic-p)
(apply 'all-the-icons-material args)))
(defsubst doom-modeline--active () (defsubst doom-modeline--active ()
"If modeline is active." "Whether is an active window."
(eq (selected-window) doom-modeline-current-window)) (eq (selected-window) doom-modeline-current-window))
;; Inspired from `powerline's `pl/make-xpm'. (defun doom-modeline--make-xpm (face width height)
(defun doom-modeline--make-xpm (color height width) "Create an XPM bitmap via FACE, WIDTH and HEIGHT. Inspired by `powerline''s `pl/make-xpm'."
"Create an XPM bitmap iva `COLOR', `HEIGHT' and `WIDTH'."
(propertize (propertize
" " 'display " " 'display
(let ((data (make-list height (make-list width 1))) (let ((data (make-list height (make-list width 1)))
(color (or color "None"))) (color (or (face-background face nil t) "None")))
(ignore-errors
(create-image (create-image
(concat (concat
(format "/* XPM */\nstatic char * percent[] = {\n\"%i %i 2 1\",\n\". c %s\",\n\" c %s\"," (format "/* XPM */\nstatic char * percent[] = {\n\"%i %i 2 1\",\n\". c %s\",\n\" c %s\","
@ -371,10 +503,12 @@ If STRICT-P, return nil if no project was found, otherwise return
if (= d 0) collect (string-to-char " ") if (= d 0) collect (string-to-char " ")
else collect (string-to-char ".")) else collect (string-to-char "."))
(if (eq idx len) "\"};" "\",\n"))))) (if (eq idx len) "\"};" "\",\n")))))
'xpm t :ascent 'center)))) 'xpm t :ascent 'center)))))
(defun doom-modeline-buffer-file-name () (defun doom-modeline-buffer-file-name ()
"Propertized variable `buffer-file-name' based on `doom-modeline-buffer-file-name-style'." "Propertized variable `buffer-file-name' based on `+doom-modeline-buffer-file-name-style'."
(let ((buffer-file-name (or buffer-file-name ""))
(buffer-file-truename (or buffer-file-truename "")))
(propertize (propertize
(pcase doom-modeline-buffer-file-name-style (pcase doom-modeline-buffer-file-name-style
(`truncate-upto-project (doom-modeline--buffer-file-name 'shrink)) (`truncate-upto-project (doom-modeline--buffer-file-name 'shrink))
@ -386,10 +520,10 @@ If STRICT-P, return nil if no project was found, otherwise return
'face 'face
(let ((face (or (and (buffer-modified-p) (let ((face (or (and (buffer-modified-p)
'doom-modeline-buffer-modified) 'doom-modeline-buffer-modified)
(and (doom-modeline--active) (and (active)
'doom-modeline-buffer-file)))) 'doom-modeline-buffer-file))))
(when face `(:inherit ,face)))))) (when face `(:inherit ,face))))))
'help-echo buffer-file-truename)) 'help-echo buffer-file-truename)))
(defun doom-modeline--buffer-file-name-truncate (&optional truncate-tail) (defun doom-modeline--buffer-file-name-truncate (&optional truncate-tail)
"Propertized variable `buffer-file-name' that truncates every dir along path. "Propertized variable `buffer-file-name' that truncates every dir along path.
@ -418,13 +552,14 @@ If TRUNCATE-TAIL is t also truncate the parent directory of the file."
(if (null root) (if (null root)
(propertize "%b" 'face (if active 'doom-modeline-buffer-file)) (propertize "%b" 'face (if active 'doom-modeline-buffer-file))
(let* ((modified-faces (if (buffer-modified-p) 'doom-modeline-buffer-modified)) (let* ((modified-faces (if (buffer-modified-p) 'doom-modeline-buffer-modified))
(relative-dirs (file-relative-name (file-name-directory buffer-file-truename) (true-filename (file-truename buffer-file-name))
(relative-dirs (file-relative-name (file-name-directory true-filename)
(if include-project (concat root "../") root))) (if include-project (concat root "../") root)))
(relative-faces (or modified-faces (if active 'doom-modeline-buffer-path))) (relative-faces (or modified-faces (if active 'doom-modeline-buffer-path)))
(file-faces (or modified-faces (if active 'doom-modeline-buffer-file)))) (file-faces (or modified-faces (if active 'doom-modeline-buffer-file))))
(if (equal "./" relative-dirs) (setq relative-dirs "")) (if (equal "./" relative-dirs) (setq relative-dirs ""))
(concat (propertize relative-dirs 'face (if relative-faces `(:inherit ,relative-faces))) (concat (propertize relative-dirs 'face (if relative-faces `(:inherit ,relative-faces)))
(propertize (file-name-nondirectory buffer-file-truename) (propertize (file-name-nondirectory true-filename)
'face (if file-faces `(:inherit ,file-faces)))))))) 'face (if file-faces `(:inherit ,file-faces))))))))
(defun doom-modeline--buffer-file-name (truncate-project-root-parent) (defun doom-modeline--buffer-file-name (truncate-project-root-parent)
@ -432,12 +567,13 @@ If TRUNCATE-TAIL is t also truncate the parent directory of the file."
If TRUNCATE-PROJECT-ROOT-PARENT is t space will be saved by truncating it down If TRUNCATE-PROJECT-ROOT-PARENT is t space will be saved by truncating it down
fish-shell style. fish-shell style.
Example: Example:
~/Projects/FOSS/emacs/lisp/comint.el => ~/P/F/emacs/lisp/comint.el" ~/Projects/FOSS/emacs/lisp/comint.el => ~/P/F/emacs/lisp/comint.el"
(let* ((project-root (doom-modeline-project-root)) (let* ((project-root (doom-modeline-project-root))
(file-name-split (shrink-path-file-mixed project-root (file-name-split (shrink-path-file-mixed project-root
(file-name-directory buffer-file-truename) (file-name-directory buffer-file-name)
buffer-file-truename)) buffer-file-name))
(active (doom-modeline--active))) (active (doom-modeline--active)))
(if (null file-name-split) (if (null file-name-split)
(propertize "%b" 'face (if active 'doom-modeline-buffer-file)) (propertize "%b" 'face (if active 'doom-modeline-buffer-file))
@ -461,10 +597,9 @@ Example:
;; ;;
;; Segments ;; buffer information
;; ;;
(doom-modeline-def-segment buffer-default-directory (doom-modeline-def-segment buffer-default-directory
"Displays `default-directory'. This is for special buffers like the scratch "Displays `default-directory'. This is for special buffers like the scratch
buffer where knowing the current project directory is important." buffer where knowing the current project directory is important."
@ -478,13 +613,11 @@ buffer where knowing the current project directory is important."
(propertize (concat " " (abbreviate-file-name default-directory)) (propertize (concat " " (abbreviate-file-name default-directory))
'face face)))) 'face face))))
;; ;;
(doom-modeline-def-segment buffer-info (doom-modeline-def-segment buffer-info
"Combined information about the current buffer, including the current working "Combined information about the current buffer, including the current working
directory, the file name, and its state (modified, read-only or non-existent)." directory, the file name, and its state (modified, read-only or non-existent)."
(concat (concat (cond (buffer-read-only
(cond (buffer-read-only
(concat (doom-modeline-maybe-icon-octicon (concat (doom-modeline-maybe-icon-octicon
"lock" "lock"
:face 'doom-modeline-warning :face 'doom-modeline-warning
@ -513,7 +646,6 @@ directory, the file name, and its state (modified, read-only or non-existent)."
(doom-modeline-buffer-file-name) (doom-modeline-buffer-file-name)
"%b"))) "%b")))
;;
(doom-modeline-def-segment buffer-info-simple (doom-modeline-def-segment buffer-info-simple
"Display only the current buffer's name, but with fontification." "Display only the current buffer's name, but with fontification."
(propertize (propertize
@ -535,7 +667,11 @@ directory, the file name, and its state (modified, read-only or non-existent)."
(t (upcase (symbol-name (plist-get sys :name)))))) (t (upcase (symbol-name (plist-get sys :name))))))
" ")) " "))
;; ;;
;; major-mode
;;
(doom-modeline-def-segment major-mode (doom-modeline-def-segment major-mode
"The major mode, including process, environment and text-scale info." "The major mode, including process, environment and text-scale info."
(propertize (propertize
@ -551,24 +687,14 @@ directory, the file name, and its state (modified, read-only or non-existent)."
'face (if (doom-modeline--active) 'doom-modeline-buffer-major-mode))) 'face (if (doom-modeline--active) 'doom-modeline-buffer-major-mode)))
(defun doom-modeline-maybe-icon-octicon (&rest args)
"Display octicon ARGS."
(when (display-graphic-p)
(apply 'all-the-icons-octicon args)))
(defun doom-modeline-maybe-icon-faicon (&rest args)
"Display fontawesome icon ARGS."
(when (display-graphic-p)
(apply 'all-the-icons-faicon args)))
(defun doom-modeline-maybe-icon-material (&rest args)
"Display material icon ARGS."
(when (display-graphic-p)
(apply 'all-the-icons-material args)))
;; ;;
(doom-modeline-def-segment vcs ;; vcs
"Displays the current branch, colored based on its state." ;;
(defvar-local doom-modeline--vcs nil)
(defun doom-modeline--update-vcs ()
"Update vsc state in mode-line."
(setq doom-modeline--vcs
(when (and vc-mode buffer-file-name) (when (and vc-mode buffer-file-name)
(let* ((backend (vc-backend buffer-file-name)) (let* ((backend (vc-backend buffer-file-name))
(state (vc-state buffer-file-name backend))) (state (vc-state buffer-file-name backend)))
@ -600,26 +726,55 @@ directory, the file name, and its state (modified, read-only or non-existent)."
" " " "
(propertize (substring vc-mode (+ (if (eq backend 'Hg) 2 3) 2)) (propertize (substring vc-mode (+ (if (eq backend 'Hg) 2 3) 2))
'face (if active face)) 'face (if active face))
" "))))) " "))))))
(add-hook 'after-save-hook #'doom-modeline--update-vcs)
(add-hook 'find-file-hook #'doom-modeline--update-vcs t)
(declare-function magit-toplevel "magit-git")
(defun doom-modeline-magit-post-refresh ()
"Update vcs state in mode-line after refreshing in magit."
(dolist (buf (buffer-list))
(when (and (not (buffer-modified-p buf))
(buffer-file-name buf)
(file-exists-p (buffer-file-name buf))
(file-in-directory-p (buffer-file-name buf) (magit-toplevel)))
(with-current-buffer buf
(vc-refresh-state)
(doom-modeline--update-vcs)))))
(add-hook 'magit-post-refresh-hook #'doom-modeline-magit-post-refresh)
(doom-modeline-def-segment vcs
"Displays the current branch, colored based on its state."
doom-modeline--vcs)
;; ;;
;; flycheck
;;
(defvar doom-modeline-vspc
(propertize " " 'face 'variable-pitch)
"Text style with icons in mode-line.")
(defun doom-modeline-icon (icon &optional text face voffset) (defun doom-modeline-icon (icon &optional text face voffset)
"Display an ICON with FACE, followed by TEXT. Use `all-the-icons-material' to fetch the icon." "Displays an ICON with FACE, followed by TEXT.
Uses `all-the-icons-material' to fetch the icon."
(concat (if vc-mode " " " ") (concat (if vc-mode " " " ")
(when icon (when icon
(concat (concat
(doom-modeline-maybe-icon-material icon :face face :height 1.1 :v-adjust (or voffset -0.2)) (doom-modeline-maybe-icon-material icon :face face :height 1.1 :v-adjust (or voffset -0.2))
(if text doom-modeline-vspc))) (if text doom-modeline-vspc)))
(when text (if text (propertize text 'face face))
(propertize text 'face face))
(if vc-mode " " " "))) (if vc-mode " " " ")))
(doom-modeline-def-segment flycheck (defvar-local doom-modeline--flycheck nil)
"Displays color-coded flycheck error status in the current buffer with pretty (add-hook 'flycheck-status-changed-functions #'doom-modeline-update-flycheck-segment)
icons." (add-hook 'flycheck-mode-hook #'doom-modeline-update-flycheck-segment)
(when (boundp 'flycheck-last-status-change)
(pcase flycheck-last-status-change (defun doom-modeline-update-flycheck-segment (&optional status)
"Update flycheck segment via STATUS."
(setq doom-modeline--flycheck
(pcase status
(`finished (if flycheck-current-errors (`finished (if flycheck-current-errors
(let-alist (flycheck-count-errors flycheck-current-errors) (let-alist (flycheck-count-errors flycheck-current-errors)
(let ((sum (+ (or .error 0) (or .warning 0)))) (let ((sum (+ (or .error 0) (or .warning 0))))
@ -632,42 +787,56 @@ icons."
(`no-checker (doom-modeline-icon "sim_card_alert" "-" 'font-lock-doc-face)) (`no-checker (doom-modeline-icon "sim_card_alert" "-" 'font-lock-doc-face))
(`errored (doom-modeline-icon "sim_card_alert" "Error" 'doom-modeline-urgent)) (`errored (doom-modeline-icon "sim_card_alert" "Error" 'doom-modeline-urgent))
(`interrupted (doom-modeline-icon "pause" "Interrupted" 'font-lock-doc-face))))) (`interrupted (doom-modeline-icon "pause" "Interrupted" 'font-lock-doc-face)))))
;; ('interrupted (doom-modeline-icon "x" "Interrupted" 'font-lock-doc-face)))))
(doom-modeline-def-segment flycheck
"Displays color-coded flycheck error status in the current buffer with pretty
icons."
doom-modeline--flycheck)
;; ;;
;; selection-info
;;
(defsubst doom-modeline-column (pos) (defsubst doom-modeline-column (pos)
"Get column of the position `POS'." "Get the column of the position `POS'."
(save-excursion (goto-char pos) (save-excursion (goto-char pos)
(current-column))) (current-column)))
(defvar-local doom-modeline-enable-word-count nil
"If non-nil, a word count will be added to the selection-info modeline
segment.")
(doom-modeline-def-segment selection-info (doom-modeline-def-segment selection-info
"Information about the current selection, such as how many characters and "Information about the current selection, such as how many characters and
lines are selected, or the NxM dimensions of a block selection." lines are selected, or the NxM dimensions of a block selection."
(when (and (doom-modeline--active) (when (and mark-active (doom-modeline--active))
(or mark-active (cl-destructuring-bind (beg . end)
(and (bound-and-true-p evil-state) (eq evil-state 'visual)))) (if (and (bound-and-true-p evil-state) (eq evil-state 'visual))
(let ((reg-beg (region-beginning)) (cons evil-visual-beginning evil-visual-end)
(reg-end (region-end))) (cons (region-beginning) (region-end)))
(propertize (propertize
(let ((lines (count-lines reg-beg (min (1+ reg-end) (point-max))))) (let ((lines (count-lines beg (min end (point-max)))))
(cond ((or (bound-and-true-p rectangle-mark-mode) (concat (cond ((or (bound-and-true-p rectangle-mark-mode)
(and (bound-and-true-p evil-visual-selection) (and (bound-and-true-p evil-visual-selection)
(eq 'block evil-visual-selection))) (eq 'block evil-visual-selection)))
(let ((cols (abs (- (doom-modeline-column reg-end) (let ((cols (abs (- (doom-modeline-column end)
(doom-modeline-column reg-beg))))) (doom-modeline-column beg)))))
(format "%dx%dB" lines cols))) (format "%dx%dB" lines cols)))
((and (bound-and-true-p evil-visual-selection) ((and (bound-and-true-p evil-visual-selection)
(eq evil-visual-selection 'line)) (eq evil-visual-selection 'line))
(format "%dL" lines)) (format "%dL" lines))
((> lines 1) ((> lines 1)
(format "%dC %dL" (- (1+ reg-end) reg-beg) lines)) (format "%dC %dL" (- end beg) lines))
(t ((format "%dC" (- end beg))))
(format "%dC" (- (1+ reg-end) reg-beg))))) (when doom-modeline-enable-word-count
(format " %dW" (count-words beg end)))))
'face 'doom-modeline-highlight)))) 'face 'doom-modeline-highlight))))
;; ;;
;; matches (anzu, evil-substitute, iedit, macro)
;;
(defun doom-modeline--macro-recording () (defun doom-modeline--macro-recording ()
"Display current Emacs or evil macro being recorded." "Display current Emacs or evil macro being recorded."
(when (and (doom-modeline--active) (or defining-kbd-macro executing-kbd-macro)) (when (and (doom-modeline--active) (or defining-kbd-macro executing-kbd-macro))
@ -685,8 +854,9 @@ lines are selected, or the NxM dimensions of a block selection."
(defsubst doom-modeline--anzu () (defsubst doom-modeline--anzu ()
"Show the match index and total number thereof. "Show the match index and total number thereof.
Require `anzu', also `evil-anzu' if using `evil-mode'
for compatibility with `evil-search'." Requires `anzu', also `evil-anzu' if using `evil-mode' for compatibility with
`evil-search'."
(setq anzu-cons-mode-line-p nil) (setq anzu-cons-mode-line-p nil)
(when (and (featurep 'anzu) (when (and (featurep 'anzu)
anzu--state anzu--state
@ -755,78 +925,79 @@ with `evil-ex-substitute', and/or 4. The number of active `iedit' regions."
(or (and (not (equal meta "")) meta) (or (and (not (equal meta "")) meta)
(if buffer-file-name " %I ")))) (if buffer-file-name " %I "))))
;; TODO Include other information
;;
;; media-info
;;
(doom-modeline-def-segment media-info (doom-modeline-def-segment media-info
"Metadata regarding the current file, such as dimensions for images." "Metadata regarding the current file, such as dimensions for images."
;; TODO Include other information
(cond ((eq major-mode 'image-mode) (cond ((eq major-mode 'image-mode)
(cl-destructuring-bind (width . height) (cl-destructuring-bind (width . height)
(image-size (image-get-display-property) :pixels) (image-size (image-get-display-property) :pixels)
(format " %dx%d " width height))))) (format " %dx%d " width height)))))
;;
;; bar
;;
(defvar doom-modeline--bar-active nil)
(defvar doom-modeline--bar-inactive nil)
(doom-modeline-def-segment bar (doom-modeline-def-segment bar
"The bar regulates the height of the mode-line in GUI Emacs. "The bar regulates the height of the mode-line in GUI Emacs.
Returns \"\" to not break --no-window-system." Returns \"\" to not break --no-window-system."
(if (display-graphic-p) (if (display-graphic-p)
(doom-modeline--make-xpm (if (doom-modeline--active)
(face-background (if (doom-modeline--active) doom-modeline--bar-active
'doom-modeline-bar doom-modeline--bar-inactive)
'doom-modeline-inactive-bar)
nil t)
doom-modeline-height
doom-modeline-bar-width)
"")) ""))
(defun doom-modeline-eyebrowse-number () (when (>= emacs-major-version 26)
"The eyebrowse number." (add-variable-watcher
(when (and (bound-and-true-p eyebrowse-mode) 'doom-modeline-height
(< 1 (length (eyebrowse--get 'window-configs)))) (lambda (_sym val op _where)
(let* ((num (eyebrowse--get 'current-slot)) (when (and (eq op 'set) (integerp val))
(tag (when num (nth 2 (assoc num (eyebrowse--get 'window-configs))))) (doom-modeline-refresh-bars doom-modeline-bar-width val))))
(str (if (and tag (< 0 (length tag)))
tag
(when num (int-to-string num)))))
str)))
(defun doom-modeline-window-bottom-left-p () (add-variable-watcher
"If it's botom left of the window." 'doom-modeline-bar-width
(let* ((edges (window-edges)) (lambda (_sym val op _where)
(minibuffer-edges (window-edges (minibuffer-window)))) (when (and (eq op 'set) (integerp val))
(and (eq 0 (car edges)) (doom-modeline-refresh-bars val doom-modeline-height)))))
(eq (nth 3 edges)
(cadr minibuffer-edges)))))
(doom-modeline-def-segment persp-number
"The persp number."
(when (featurep 'persp-mode)
(when (doom-modeline-window-bottom-left-p)
(let ((persp (get-current-persp)))
(propertize
(concat
(number-to-string
(+ 1
(cl-position
(persp-name persp)
(persp-names-current-frame-fast-ordered))))
"."
(or (doom-modeline-eyebrowse-number) "1")
" ")
'face 'doom-modeline-persp)))))
;;
;; window number
;;
(advice-add #'window-numbering-install-mode-line :override #'ignore) (advice-add #'window-numbering-install-mode-line :override #'ignore)
(advice-add #'window-numbering-clear-mode-line :override #'ignore) (advice-add #'window-numbering-clear-mode-line :override #'ignore)
(doom-modeline-def-segment window-number (doom-modeline-def-segment window-number
(if (bound-and-true-p window-numbering-mode) (setq winum-auto-setup-mode-line nil)
(propertize (format " %s " (window-numbering-get-number-string)) (let ((num (cond
((bound-and-true-p winum-mode)
(winum-get-number-string))
((bound-and-true-p window-numbering-mode)
(window-numbering-get-number-string))
(t ""))))
(if (< 0 (length num))
(propertize (format " %s " num)
'face (if (doom-modeline--active) 'face (if (doom-modeline--active)
'doom-modeline-bar 'doom-modeline-bar
'doom-modeline-inactive-bar)) 'doom-modeline-inactive-bar))
"")) "")))
;;
;; workspace-number
;;
(declare-function eyebrowse--get 'eyebrowse) (declare-function eyebrowse--get 'eyebrowse)
(doom-modeline-def-segment workspace-number (doom-modeline-def-segment workspace-number
"The current workspace name or number. Requires `eyebrowse-mode' to be enabled." "The current workspace name or number. Requires `eyebrowse-mode' to be
enabled."
(if (and (bound-and-true-p eyebrowse-mode) (if (and (bound-and-true-p eyebrowse-mode)
(< 1 (length (eyebrowse--get 'window-configs)))) (< 1 (length (eyebrowse--get 'window-configs))))
(let* ((num (eyebrowse--get 'current-slot)) (let* ((num (eyebrowse--get 'current-slot))
@ -850,40 +1021,68 @@ with `evil-ex-substitute', and/or 4. The number of active `iedit' regions."
(media-info major-mode)) (media-info major-mode))
(doom-modeline-def-modeline special (doom-modeline-def-modeline special
(bar matches " " buffer-info-simple " %l:%c %p " selection-info) (window-number bar matches " " buffer-info-simple " %l:%c %p " selection-info)
(buffer-encoding major-mode flycheck)) (buffer-encoding major-mode flycheck))
(doom-modeline-def-modeline project (doom-modeline-def-modeline project
(bar buffer-default-directory) (window-number bar buffer-default-directory)
(major-mode)) (major-mode))
(doom-modeline-def-modeline media (doom-modeline-def-modeline media
(bar " %b ") (window-number bar " %b ")
(media-info major-mode)) (media-info major-mode))
;;
;; Hooks
;;
(defun doom-modeline-refresh-bars (&optional width height)
"Refreash mode-line bars with `WIDTH' and `HEIGHT'."
(setq doom-modeline--bar-active
(doom-modeline--make-xpm 'doom-modeline-bar
(or width doom-modeline-bar-width)
(or height doom-modeline-height))
doom-modeline--bar-inactive
(doom-modeline--make-xpm 'doom-modeline-inactive-bar
(or width doom-modeline-bar-width)
(or height doom-modeline-height))))
;;;###autoload
(defun doom-modeline-init ()
"Initialize doom mode-line."
;; Create bars
(doom-modeline-refresh-bars)
(unless after-init-time
;; These buffers are already created and don't get modelines. For the love
;; of Emacs, someone give the man a modeline!
(dolist (bname '("*scratch*" "*Messages*"))
(with-current-buffer bname
(doom-modeline-set-modeline 'main)))))
;;;###autoload
(defun doom-modeline-set-special-modeline ()
"Set sepcial mode-line."
(doom-modeline-set-modeline 'special))
;;;###autoload
(defun doom-modeline-set-media-modeline ()
"Set media mode-line."
(doom-modeline-set-modeline 'media))
;;;###autoload
(defun doom-modeline-set-project-modeline ()
"Set project mode-line."
(doom-modeline-set-modeline 'project))
;; ;;
;; Bootstrap ;; Bootstrap
;; ;;
;;;###autoload (doom-modeline-set-modeline 'main t) ; set default modeline
(defun doom-modeline-init ()
"Set the default modeline."
(doom-modeline-set-modeline 'main t)
;; This scratch and messages buffer is already created and doesn't get a ;; (add-hook 'doom-load-theme-hook #'doom-modeline-init)
;; modeline. ;; (add-hook 'doom-scratch-buffer-hook #'doom-modeline-set-special-modeline)
(with-current-buffer "*Messages*" ;; (add-hook 'doom-dashboard-mode-hook #'doom-modeline-set-project-modeline)
(doom-modeline-set-modeline 'main))
(with-current-buffer "*scratch*"
(doom-modeline-set-modeline 'main)))
(defun doom-modeline-set-special-modeline ()
"Set the special modeline."
(doom-modeline-set-modeline 'special))
(defun doom-modeline-set-media-modeline ()
"Set the media modeline."
(doom-modeline-set-modeline 'media))
(add-hook 'image-mode-hook #'doom-modeline-set-media-modeline) (add-hook 'image-mode-hook #'doom-modeline-set-media-modeline)
(add-hook 'org-src-mode-hook #'doom-modeline-set-special-modeline) (add-hook 'org-src-mode-hook #'doom-modeline-set-special-modeline)
@ -892,13 +1091,31 @@ with `evil-ex-substitute', and/or 4. The number of active `iedit' regions."
;; Versions, support Python, Ruby and Golang ;; Versions, support Python, Ruby and Golang
(add-hook 'python-mode-hook (add-hook 'python-mode-hook
(lambda () (lambda ()
(setq doom-modeline-env-command "python --version 2>&1 | cut -d' ' -f2"))) (when (executable-find "python")
(setq doom-modeline-env-command "python --version 2>&1 | cut -d' ' -f2 | sed -n '1p'"))))
(add-hook 'ruby-mode-hook (add-hook 'ruby-mode-hook
(lambda () (lambda ()
(setq doom-modeline-env-command "ruby --version 2>&1 | cut -d' ' -f2"))) (when (executable-find "ruby")
(setq doom-modeline-env-command "ruby --version 2>&1 | cut -d' ' -f2 | sed -n '1p'"))))
(add-hook 'go-mode-hook (add-hook 'go-mode-hook
(lambda () (lambda ()
(setq doom-modeline-env-command "go version 2>&1 | cut -d' ' -f3 | tr -d 'go'"))) (when (executable-find "go")
(setq doom-modeline-env-command "go version 2>&1 | cut -d' ' -f3 | tr -d 'go' | sed -n '1p'"))))
;; Ensure modeline is inactive when Emacs is unfocused (and active otherwise)
(defvar doom-modeline-remap-face-cookie nil)
(defun doom-modeline-focus ()
"Focus mode-line."
(when doom-modeline-remap-face-cookie
(require 'face-remap)
(face-remap-remove-relative doom-modeline-remap-face-cookie)))
(defun doom-modeline-unfocus ()
"Unfocus mode-line."
(setq doom-modeline-remap-face-cookie (face-remap-add-relative 'mode-line 'mode-line-inactive)))
(add-hook 'focus-in-hook #'doom-modeline-focus)
(add-hook 'focus-out-hook #'doom-modeline-unfocus)
(provide 'doom-modeline) (provide 'doom-modeline)