diff --git a/doom-modeline.el b/doom-modeline.el index a69428d..21b9b8c 100644 --- a/doom-modeline.el +++ b/doom-modeline.el @@ -1,12 +1,12 @@ -;;; doom-modeline.el --- A minimal modeline from DOOM Emacs. -*- lexical-binding: t; -*- +;;; doom-modeline.el --- A minimal modeline from DOOM. -*- lexical-binding: t; -*- ;; Copyright (C) 2018 Vincent Zhang ;; Author: Vincent Zhang ;; URL: https://github.com/seagle0128/doom-modeline ;; Version: 0.2.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")) -;; Keywords: modeline mode-line doom +;; 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 ;; This file is not part of GNU Emacs. @@ -62,104 +62,92 @@ (defvar doom-modeline-buffer-file-name-style 'truncate-upto-project "Determines the style used by `doom-modeline-buffer-file-name'. -Given ~/Projects/FOSS/emacs/lisp/comint.el -truncate-upto-project => ~/P/F/emacs/lisp/comint.el -truncate-upto-root => ~/P/F/e/lisp/comint.el -truncate-all => ~/P/F/e/l/comint.el -relative-from-project => emacs/lisp/comint.el -relative-to-project => lisp/comint.el -file-name => comint.el") + Given ~/Projects/FOSS/emacs/lisp/comint.el + truncate-upto-project => ~/P/F/emacs/lisp/comint.el + truncate-upto-root => ~/P/F/e/lisp/comint.el + truncate-all => ~/P/F/e/l/comint.el + relative-from-project => emacs/lisp/comint.el + relative-to-project => lisp/comint.el + file-name => comint.el") ;; externs -(defvar anzu--current-position 0) -(defvar anzu--overflow-p nil) -(defvar anzu--state nil) -(defvar anzu--total-matched 0) -(defvar evil-ex-active-highlights-alist nil) -(defvar evil-ex-argument nil) -(defvar evil-ex-range nil) -(defvar evil-mode nil) -(defvar evil-state nil) -(defvar evil-visual-beginning nil) -(defvar evil-visual-end nil) -(defvar evil-visual-selection nil) -(defvar iedit-mode nil) -(defvar iedit-occurrences-overlays nil) +(defvar anzu--current-position) +(defvar anzu--overflow-p) +(defvar anzu--state) +(defvar anzu--total-matched) +(defvar anzu-cons-mode-line-p) +(defvar anzu-minimum-input-length) +(defvar anzu-search-threshold) +(defvar evil-ex-active-highlights-alist) +(defvar evil-ex-argument) +(defvar evil-ex-range) +(defvar evil-mode) +(defvar evil-state) +(defvar evil-visual-beginning) +(defvar evil-visual-end) +(defvar evil-visual-selection) +(defvar flycheck-current-errors) +(defvar iedit-mode) +(defvar iedit-occurrences-overlays) (defvar text-scale-mode-amount) -(defvar-local flycheck-current-errors nil) - - ;; ;; Custom faces ;; (defgroup doom-modeline nil - "TODO" + "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." - :group 'doom-modeline) + "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." - :group 'doom-modeline) + "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." - :group 'doom-modeline) + "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." - :group 'doom-modeline) + "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." - :group 'doom-modeline) + "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'" - :group 'doom-modeline) +`iedit'") (defface doom-modeline-info `((t (:inherit (success bold)))) - "Face for info-level messages in the modeline. Used by `*vc'." - :group 'doom-modeline) + "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'" - :group 'doom-modeline) + "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'" - :group 'doom-modeline) + "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." - :group 'doom-modeline) + "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." - :group 'doom-modeline) +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." - :group 'doom-modeline) + "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." - :group 'doom-modeline) + "The face used for eyebrowse.") (eval-and-compile (defun doom-modeline--resolve-hook-forms (hooks) @@ -193,7 +181,7 @@ active." (defvar doom-modeline-var-alist ())) (defmacro doom-modeline-def-segment (name &rest body) - "Defines a modeline segment and byte compiles it." + "Defines a modeline segment NAME with BODY and byte compiles it." (declare (indent defun) (doc-string 2)) (let ((sym (intern (format "doom-modeline-segment--%s" name))) (docstring (if (stringp (car body)) @@ -229,10 +217,11 @@ active." (defmacro doom-modeline-def-modeline (name lhs &optional rhs) "Defines a modeline format and byte-compiles it. -NAME is a symbol to identify it (used by `doom-modeline' for retrieval). -LHS and RHS are lists of symbols of modeline segments defined with - `doom-modeline-def-segment'. -Example: + + NAME is a symbol to identify it (used by `doom-modeline' for retrieval). + LHS and RHS are lists of symbols of modeline segments defined with + `doom-modeline-def-segment'. + Example: (doom-modeline-def-modeline minimal (bar matches \" \" buffer-info) (media-info major-mode)) @@ -262,14 +251,16 @@ Example: (defun doom-modeline (key) "Return a mode-line configuration associated with KEY (a symbol). -Throws an error if it doesn't exist." + + Throws an error if it doesn't exist." (let ((fn (intern (format "doom-modeline-format--%s" key)))) (when (functionp fn) `(:eval (,fn))))) (defun doom-modeline-set-modeline (key &optional default) "Set the modeline format. Does nothing if the modeline KEY doesn't exist. -If DEFAULT is non-nil, set the default mode-line for all buffers." + + If DEFAULT is non-nil, set the default mode-line for all buffers." (let ((modeline (doom-modeline key))) (setf (if default (default-value 'mode-line-format) @@ -278,7 +269,8 @@ If DEFAULT is non-nil, set the default mode-line for all buffers." (defun doom-modeline-project-root () "Get the path to the root of your project. -If STRICT-P, return nil if no project was found, otherwise return + + If STRICT-P, return nil if no project was found, otherwise return `default-directory'." (let (projectile-require-project-root) (projectile-project-root))) @@ -404,7 +396,7 @@ If STRICT-P, return nil if no project was found, otherwise return (eq (selected-window) doom-modeline-current-window)) (defun doom-modeline--make-xpm (face width height) - "Create an XPM bitmap. Inspired by `powerline''s `pl/make-xpm'." + "Create an XPM bitmap via FACE, WIDTH and HEIGHT. Inspired by `powerline''s `pl/make-xpm'." (propertize " " 'display (let ((data (make-list height (make-list width 1))) @@ -452,7 +444,8 @@ If STRICT-P, return nil if no project was found, otherwise return (defun doom-modeline--buffer-file-name-truncate (&optional truncate-tail) "Propertized variable `buffer-file-name' that truncates every dir along path. -If TRUNCATE-TAIL is t also truncate the parent directory of the file." + + If TRUNCATE-TAIL is t also truncate the parent directory of the file." (let ((dirs (shrink-path-prompt (file-name-directory buffer-file-truename))) (active (doom-modeline--active))) (if (null dirs) @@ -470,7 +463,7 @@ If TRUNCATE-TAIL is t also truncate the parent directory of the file." 'face (if file-faces `(:inherit ,file-faces))))))))) (defun doom-modeline--buffer-file-name-relative (&optional include-project) - "Propertized variable `buffer-file-name' showing directories relative to project's root only." + "Propertized variable `buffer-file-name' showing directories relative to INCLUDE-PROJECT root only." (let ((root (doom-modeline-project-root)) (active (doom-modeline--active))) (if (null root) @@ -488,11 +481,12 @@ If TRUNCATE-TAIL is t also truncate the parent directory of the file." (defun doom-modeline--buffer-file-name (truncate-project-root-parent) "Propertized variable `buffer-file-name'. -If TRUNCATE-PROJECT-ROOT-PARENT is t space will be saved by truncating it down -fish-shell style. -Example: -~/Projects/FOSS/emacs/lisp/comint.el => ~/P/F/emacs/lisp/comint.el" + If TRUNCATE-PROJECT-ROOT-PARENT is t space will be saved by truncating it down + fish-shell style. + + Example: + ~/Projects/FOSS/emacs/lisp/comint.el => ~/P/F/emacs/lisp/comint.el" (let* ((project-root (doom-modeline-project-root)) (file-name-split (shrink-path-file-mixed project-root (file-name-directory buffer-file-name) @@ -613,40 +607,61 @@ directory, the file name, and its state (modified, read-only or non-existent)." ;; vcs ;; +(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) + (let* ((backend (vc-backend buffer-file-name)) + (state (vc-state buffer-file-name backend))) + (let ((face 'mode-line-inactive) + (active (doom-modeline--active)) + (all-the-icons-default-adjust -0.1)) + (concat (if (display-graphic-p) " ") + (cond ((memq state '(edited added)) + (if active (setq face 'doom-modeline-info)) + (doom-modeline-maybe-icon-octicon + "git-compare" + :face face + :v-adjust -0.05)) + ((eq state 'needs-merge) + (if active (setq face 'doom-modeline-info)) + (doom-modeline-maybe-icon-octicon "git-merge" :face face)) + ((eq state 'needs-update) + (if active (setq face 'doom-modeline-warning)) + (doom-modeline-maybe-icon-octicon "arrow-down" :face face)) + ((memq state '(removed conflict unregistered)) + (if active (setq face 'doom-modeline-urgent)) + (doom-modeline-maybe-icon-octicon "alert" :face face)) + (t + (if active (setq face 'font-lock-doc-face)) + (doom-modeline-maybe-icon-octicon + "git-branch" + :face face + :v-adjust -0.05))) + " " + (propertize (substring vc-mode (+ (if (eq backend 'Hg) 2 3) 2)) + '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." - (when (and vc-mode buffer-file-name) - (let* ((backend (vc-backend buffer-file-name)) - (state (vc-state buffer-file-name backend))) - (let ((face 'mode-line-inactive) - (active (doom-modeline--active)) - (all-the-icons-default-adjust -0.1)) - (concat (if (display-graphic-p) " ") - (cond ((memq state '(edited added)) - (if active (setq face 'doom-modeline-info)) - (doom-modeline-maybe-icon-octicon - "git-compare" - :face face - :v-adjust -0.05)) - ((eq state 'needs-merge) - (if active (setq face 'doom-modeline-info)) - (doom-modeline-maybe-icon-octicon "git-merge" :face face)) - ((eq state 'needs-update) - (if active (setq face 'doom-modeline-warning)) - (doom-modeline-maybe-icon-octicon "arrow-down" :face face)) - ((memq state '(removed conflict unregistered)) - (if active (setq face 'doom-modeline-urgent)) - (doom-modeline-maybe-icon-octicon "alert" :face face)) - (t - (if active (setq face 'font-lock-doc-face)) - (doom-modeline-maybe-icon-octicon - "git-branch" - :face face - :v-adjust -0.05))) - " " - (propertize (substring vc-mode (+ (if (eq backend 'Hg) 2 3) 2)) - 'face (if active face)) - " "))))) + doom-modeline--vcs) ;; @@ -713,17 +728,19 @@ segment.") lines are selected, or the NxM dimensions of a block selection." (when (and mark-active (doom-modeline--active)) (cl-destructuring-bind (beg . end) - (if (eq evil-state 'visual) + (if (and (bound-and-true-p evil-state) (eq evil-state 'visual)) (cons evil-visual-beginning evil-visual-end) (cons (region-beginning) (region-end))) (propertize (let ((lines (count-lines beg (min end (point-max))))) (concat (cond ((or (bound-and-true-p rectangle-mark-mode) - (eq 'block evil-visual-selection)) + (and (bound-and-true-p evil-visual-selection) + (eq 'block evil-visual-selection))) (let ((cols (abs (- (doom-modeline-column end) (doom-modeline-column beg))))) (format "%dx%dB" lines cols))) - ((eq evil-visual-selection 'line) + ((and (bound-and-true-p evil-visual-selection) + (eq evil-visual-selection 'line)) (format "%dL" lines)) ((> lines 1) (format "%dC %dL" (- end beg) lines)) @@ -753,10 +770,13 @@ lines are selected, or the NxM dimensions of a block selection." (defsubst doom-modeline--anzu () "Show the match index and total number thereof. -Requires `anzu', also `evil-anzu' if using `evil-mode' for compatibility with + + Requires `anzu', also `evil-anzu' if using `evil-mode' for compatibility with `evil-search'." (setq anzu-cons-mode-line-p nil) - (when (and anzu--state (not iedit-mode)) + (when (and (featurep 'anzu) + anzu--state + (not (bound-and-true-p iedit-mode))) (propertize (let ((here anzu--current-position) (total anzu--total-matched)) @@ -772,7 +792,8 @@ Requires `anzu', also `evil-anzu' if using `evil-mode' for compatibility with (defsubst doom-modeline--evil-substitute () "Show number of matches for evil-ex substitutions and highlights in real time." - (when (and evil-mode + (when (and (featurep 'evil) + evil-mode (or (assq 'evil-ex-substitute evil-ex-active-highlights-alist) (assq 'evil-ex-global-match evil-ex-active-highlights-alist) (assq 'evil-ex-buffer-match evil-ex-active-highlights-alist))) @@ -787,11 +808,12 @@ Requires `anzu', also `evil-anzu' if using `evil-mode' for compatibility with 'face (if (doom-modeline--active) 'doom-modeline-panel)))) (defun doom-modeline-themes--overlay-sort (a b) + "Sort overlay A and B." (< (overlay-start a) (overlay-start b))) (defsubst doom-modeline--iedit () "Show the number of iedit regions matches + what match you're on." - (when (and iedit-mode iedit-occurrences-overlays) + (when (and (featurep 'iedit) iedit-mode iedit-occurrences-overlays) (propertize (let ((this-oc (or (let ((inhibit-message t)) (iedit-find-current-occurrence-overlay))