Last active
May 7, 2019 21:54
-
-
Save aaronjensen/32eb280a2ab67201dc350d6e392a3b65 to your computer and use it in GitHub Desktop.
doom modeline for spacemacs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
# Add to spacemacs-configuration-layers: | |
# theming | |
# doom-modeline | |
# Add to dotspacemacs-excluded-packages | |
# spaceline | |
# Set: | |
# dotspacemacs-mode-line-theme 'vanilla | |
# customizations for monokai: | |
(setq theming-modifications | |
`((monokai | |
(doom-modeline-bar :background "#F92672") | |
(doom-modeline-bracket :foreground "#BDBAAD") | |
(doom-modeline-panel :background "#F92672") | |
(doom-modeline-persp :foreground "#F8F8F2") | |
(spacemacs-normal-face :background "#F92672")))) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
;;; -*- lexical-binding: t; -*- | |
(require 'all-the-icons) | |
(require 'memoize) | |
(require 's) | |
(require 'powerline-separators) | |
(defvar powerline-image-apple-rgb) | |
(eval-and-compile | |
(defun doom--resolve-hooks (hooks) | |
(cl-loop with quoted-p = (eq (car-safe hooks) 'quote) | |
for hook in (doom-enlist (doom-unquote hooks)) | |
if (eq (car-safe hook) 'quote) | |
collect (cadr hook) | |
else if quoted-p | |
collect hook | |
else collect (intern (format "%s-hook" (symbol-name hook))))) | |
(defun doom-enlist (exp) | |
"Return EXP wrapped in a list, or as-is if already a list." | |
(if (listp exp) exp (list exp))) | |
(defun doom-unquote (exp) | |
"Return EXP unquoted." | |
(while (memq (car-safe exp) '(quote function)) | |
(setq exp (cadr exp))) | |
exp) | |
(defvar doom--transient-counter 0)) | |
(defmacro add-transient-hook! (hook &rest forms) | |
"Attaches transient forms to a HOOK. | |
HOOK can be a quoted hook or a sharp-quoted function (which will be advised). | |
These forms will be evaluated once when that function/hook is first invoked, | |
then it detaches itself." | |
(declare (indent 1)) | |
(let ((append (eq (car forms) :after)) | |
(fn (intern (format "doom-transient-hook-%s" (cl-incf doom--transient-counter))))) | |
`(when ,hook | |
(fset ',fn | |
(lambda (&rest _) | |
,@forms | |
(cond ((functionp ,hook) (advice-remove ,hook #',fn)) | |
((symbolp ,hook) (remove-hook ,hook #',fn))) | |
(unintern ',fn nil))) | |
(cond ((functionp ,hook) | |
(advice-add ,hook ,(if append :after :before) #',fn)) | |
((symbolp ,hook) | |
(add-hook ,hook #',fn ,append)))))) | |
(defmacro add-hook! (&rest args) | |
"A convenience macro for `add-hook'. Takes, in order: | |
1. Optional properties :local and/or :append, which will make the hook | |
buffer-local or append to the list of hooks (respectively), | |
2. The hooks: either an unquoted major mode, an unquoted list of major-modes, | |
a quoted hook variable or a quoted list of hook variables. If unquoted, the | |
hooks will be resolved by appending -hook to each symbol. | |
3. A function, list of functions, or body forms to be wrapped in a lambda. | |
Examples: | |
(add-hook! 'some-mode-hook 'enable-something) | |
(add-hook! some-mode '(enable-something and-another)) | |
(add-hook! '(one-mode-hook second-mode-hook) 'enable-something) | |
(add-hook! (one-mode second-mode) 'enable-something) | |
(add-hook! :append (one-mode second-mode) 'enable-something) | |
(add-hook! :local (one-mode second-mode) 'enable-something) | |
(add-hook! (one-mode second-mode) (setq v 5) (setq a 2)) | |
(add-hook! :append :local (one-mode second-mode) (setq v 5) (setq a 2)) | |
Body forms can access the hook's arguments through the let-bound variable | |
`args'." | |
(declare (indent defun) (debug t)) | |
(let ((hook-fn 'add-hook) | |
append-p local-p) | |
(while (keywordp (car args)) | |
(pcase (pop args) | |
(:append (setq append-p t)) | |
(:local (setq local-p t)) | |
(:remove (setq hook-fn 'remove-hook)))) | |
(let ((hooks (doom--resolve-hooks (pop args))) | |
(funcs | |
(let ((val (car args))) | |
(if (memq (car-safe val) '(quote function)) | |
(if (cdr-safe (cadr val)) | |
(cadr val) | |
(list (cadr val))) | |
(list args)))) | |
forms) | |
(dolist (fn funcs) | |
(setq fn (if (symbolp fn) | |
`(function ,fn) | |
`(lambda (&rest _) ,@args))) | |
(dolist (hook hooks) | |
(push (cond ((eq hook-fn 'remove-hook) | |
`(remove-hook ',hook ,fn ,local-p)) | |
(t | |
`(add-hook ',hook ,fn ,append-p ,local-p))) | |
forms))) | |
`(progn ,@(nreverse forms))))) | |
(defmacro def-modeline-segment! (name &rest forms) | |
"Defines a modeline segment and byte compiles it." | |
(declare (indent defun) (doc-string 2)) | |
(let ((sym (intern (format "doom-modeline-segment--%s" name)))) | |
`(progn | |
(defun ,sym () ,@forms) | |
,(unless (bound-and-true-p byte-compile-current-file) | |
`(let (byte-compile-warnings) | |
(byte-compile #',sym)))))) | |
(defsubst doom--prepare-modeline-segments (segments) | |
(cl-loop for seg in segments | |
if (stringp seg) | |
collect seg | |
else | |
collect (list (intern (format "doom-modeline-segment--%s" (symbol-name seg)))))) | |
(defmacro 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 `def-modeline-segment!'. | |
Example: | |
(def-modeline! minimal | |
(bar matches \" \" buffer-info) | |
(media-info major-mode)) | |
(doom-set-modeline 'minimal t)" | |
(let ((sym (intern (format "doom-modeline-format--%s" name))) | |
(lhs-forms (doom--prepare-modeline-segments lhs)) | |
(rhs-forms (doom--prepare-modeline-segments rhs))) | |
`(progn | |
(defun ,sym () | |
(let ((lhs (list ,@lhs-forms)) | |
(rhs (list ,@rhs-forms))) | |
(let ((rhs-str (format-mode-line rhs))) | |
(list lhs | |
(propertize | |
" " 'display | |
`((space :align-to (- (+ right right-fringe right-margin) | |
,(+ 1 (string-width rhs-str)))))) | |
rhs-str)))) | |
,(unless (bound-and-true-p byte-compile-current-file) | |
`(let (byte-compile-warnings) | |
(byte-compile #',sym)))))) | |
(defun doom-modeline (key) | |
"Returns 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)))) | |
(when (functionp fn) | |
`(:eval (,fn))))) | |
(defun doom-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." | |
(when-let* ((modeline (doom-modeline key))) | |
(setf (if default | |
(default-value 'mode-line-format) | |
(buffer-local-value 'mode-line-format (current-buffer))) | |
modeline))) | |
;; anzu and evil-anzu expose current/total state that can be displayed in the | |
;; mode-line. | |
(use-package evil-anzu | |
:when (featurep 'evil) | |
:defer t | |
:init | |
(add-transient-hook! #'evil-ex-start-search (require 'evil-anzu)) | |
:config | |
(setq anzu-cons-mode-line-p nil | |
anzu-minimum-input-length 1 | |
anzu-search-threshold 250) | |
;; Avoid anzu conflicts across buffers | |
(mapc #'make-variable-buffer-local | |
'(anzu--total-matched anzu--current-position anzu--state | |
anzu--cached-count anzu--cached-positions anzu--last-command | |
anzu--last-isearch-string anzu--overflow-p)) | |
;; Ensure anzu state is cleared when searches & iedit are done | |
(add-hook 'isearch-mode-end-hook #'anzu--reset-status t) | |
(add-hook '+evil-esc-hook #'anzu--reset-status t) | |
(add-hook 'iedit-mode-end-hook #'anzu--reset-status)) | |
;; Keep `+doom-modeline-current-window' up-to-date | |
(defvar +doom-modeline-current-window (frame-selected-window)) | |
(defun +doom-modeline|set-selected-window (&rest _) | |
"Sets `+doom-modeline-current-window' appropriately" | |
(let ((win (frame-selected-window))) | |
(unless (minibuffer-window-active-p win) | |
(setq +doom-modeline-current-window win)))) | |
(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 #'select-window :after #'+doom-modeline|set-selected-window) | |
;; fish-style modeline | |
(use-package shrink-path | |
:commands (shrink-path-prompt shrink-path-file-mixed)) | |
;; | |
;; Variables | |
;; | |
(defvar +doom-modeline-height 29 | |
"How tall the mode-line should be (only respected in GUI emacs).") | |
(defvar +doom-modeline-bar-width 3 | |
"How wide the mode-line bar should be (only respected in GUI emacs).") | |
(defvar +doom-modeline-vspc | |
(propertize " " 'face 'variable-pitch) | |
"TODO") | |
(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-to-project => lisp/comint.el | |
file-name => comint.el") | |
;; externs | |
(defvar anzu--state nil) | |
(defvar evil-mode nil) | |
(defvar evil-state nil) | |
(defvar evil-visual-selection nil) | |
(defvar iedit-mode nil) | |
(defvar all-the-icons-scale-factor) | |
(defvar all-the-icons-default-adjust) | |
;; | |
;; Custom faces | |
;; | |
(defgroup +doom-modeline nil | |
"" | |
:group 'doom) | |
(defface doom-modeline-buffer-path | |
'((t (:inherit mode-line-emphasis :bold t))) | |
"Face used for the dirname part of the buffer path." | |
:group '+doom-modeline) | |
(defface doom-modeline-buffer-file | |
'((t (:inherit mode-line-buffer-id))) | |
"Face used for the filename part of the mode-line buffer path." | |
:group '+doom-modeline) | |
(defface doom-modeline-buffer-modified | |
'((t (:inherit error :background nil :bold t))) | |
"Face used for the 'unsaved' symbol in the mode-line." | |
:group '+doom-modeline) | |
(defface doom-modeline-buffer-major-mode | |
'((t (:inherit mode-line-emphasis :bold t))) | |
"Face used for the major-mode segment in the mode-line." | |
:group '+doom-modeline) | |
(defface doom-modeline-highlight | |
'((t (:inherit mode-line-emphasis))) | |
"Face for bright segments of the mode-line." | |
:group '+doom-modeline) | |
(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) | |
(defface doom-modeline-info | |
`((t (:inherit success :bold t))) | |
"Face for info-level messages in the modeline. Used by `*vc'." | |
:group '+doom-modeline) | |
(defface doom-modeline-warning | |
`((t (:inherit warning :bold t))) | |
"Face for warnings in the modeline. Used by `*flycheck'" | |
:group '+doom-modeline) | |
(defface doom-modeline-urgent | |
`((t (:inherit error :bold t))) | |
"Face for errors in the modeline. Used by `*flycheck'" | |
:group '+doom-modeline) | |
;; 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) | |
(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) | |
(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) | |
(defface doom-modeline-persp '((t ())) | |
"The face used for persp number." | |
:group '+doom-modeline) | |
(defface doom-modeline-bracket '((t (:inherit shadow))) | |
"The face used for brackets around the project." | |
:group '+doom-modeline) | |
;; | |
;; Bootstrap | |
;; | |
;; Show version string for multi-version managers like rvm, rbenv, pyenv, etc. | |
(defvar-local +doom-modeline-env-version nil) | |
(defvar-local +doom-modeline-env-command nil) | |
(add-hook! '(focus-in-hook find-file-hook) #'+doom-modeline|update-env) | |
(defun +doom-modeline|update-env () | |
(when +doom-modeline-env-command | |
(let* ((default-directory (projectile-project-root)) | |
(s (shell-command-to-string +doom-modeline-env-command))) | |
(setq +doom-modeline-env-version (if (string-match "[ \t\n\r]+\\'" s) | |
(replace-match "" t t s) | |
s))))) | |
;; Only support python and ruby for now | |
(add-hook! 'python-mode-hook (setq +doom-modeline-env-command "python --version 2>&1 | cut -d' ' -f2")) | |
(add-hook! 'ruby-mode-hook (setq +doom-modeline-env-command "ruby --version 2>&1 | cut -d' ' -f2")) | |
;; | |
;; Modeline helpers | |
;; | |
(defsubst active () | |
(eq (selected-window) +doom-modeline-current-window)) | |
;; Inspired from `powerline's `pl/make-xpm'. | |
(defmemoize +doom-modeline--make-xpm (color height width) | |
"Create an XPM bitmap." | |
(let ((color (pl/hex-color color))) | |
(propertize | |
" " 'display | |
(let ((data (make-list height (make-list width 1))) | |
(color (or color "None"))) | |
(create-image | |
(concat | |
(format "/* XPM */\nstatic char * percent[] = {\n\"%i %i 2 1\",\n\". c %s\",\n\" c %s\"," | |
(length (car data)) | |
(length data) | |
color | |
color) | |
(apply #'concat | |
(cl-loop with idx = 0 | |
with len = (length data) | |
for dl in data | |
do (cl-incf idx) | |
collect | |
(concat "\"" | |
(cl-loop for d in dl | |
if (= d 0) collect (string-to-char " ") | |
else collect (string-to-char ".")) | |
(if (eq idx len) "\"};" "\",\n"))))) | |
'xpm t :ascent 'center))))) | |
(defun +doom-modeline-buffer-file-name () | |
"Propertized `buffer-file-name' based on `+doom-modeline-buffer-file-name-style'." | |
(propertize | |
(pcase +doom-modeline-buffer-file-name-style | |
('truncate-upto-project (+doom-modeline--buffer-file-name 'shrink)) | |
('truncate-upto-root (+doom-modeline--buffer-file-name-truncate)) | |
('truncate-all (+doom-modeline--buffer-file-name-truncate t)) | |
('relative-to-project (+doom-modeline--buffer-file-name-relative)) | |
('file-name (propertize (file-name-nondirectory buffer-file-name) | |
'face | |
(let ((face (or (and (buffer-modified-p) | |
'doom-modeline-buffer-modified) | |
(and (active) | |
'doom-modeline-buffer-file)))) | |
(when face `(:inherit ,face)))))) | |
'help-echo (+doom-modeline--buffer-file-name nil))) | |
(defun +doom-modeline--buffer-file-name-truncate (&optional truncate-tail) | |
"Propertized `buffer-file-name' that truncates every dir along path. | |
If TRUNCATE-TAIL is t also truncate the parent directory of the file." | |
(let ((dirs (shrink-path-prompt (file-name-directory | |
(or buffer-file-truename | |
(file-truename buffer-file-name)))))) | |
(if (null dirs) | |
"%b" | |
(let ((modified-faces (if (buffer-modified-p) 'doom-modeline-buffer-modified)) | |
(active (active))) | |
(let ((dirname (car dirs)) | |
(basename (cdr dirs)) | |
(dir-faces (or modified-faces (if active 'doom-modeline-project-root-dir))) | |
(file-faces (or modified-faces (if active 'doom-modeline-buffer-file)))) | |
(concat (propertize dirname | |
'face (if dir-faces `(:inherit ,dir-faces))) | |
(propertize (concat (if truncate-tail (substring basename 0 1) basename) "/") | |
'face (if dir-faces `(:inherit ,dir-faces))) | |
(propertize (file-name-nondirectory buffer-file-name) | |
'face (if file-faces `(:inherit ,file-faces))))))))) | |
(defmemoize +doom-file-relative-name (filename directory) | |
(file-relative-name filename directory)) | |
(defun +doom-modeline--buffer-file-name-relative () | |
"Propertized `buffer-file-name' showing directories relative to project's root only." | |
(let* ((projectile-require-project-root nil) | |
(root (projectile-project-root))) | |
(if (null root) | |
"%b" | |
(let* ((modified-faces (if (buffer-modified-p) 'doom-modeline-buffer-modified)) | |
(active (active)) | |
(relative-dirs (+doom-file-relative-name (file-name-directory buffer-file-name) root)) | |
(relative-faces (or modified-faces (if active 'doom-modeline-buffer-path))) | |
(file-faces (or modified-faces (if active 'doom-modeline-buffer-file)))) | |
(if (equal "./" relative-dirs) (setq relative-dirs "")) | |
(concat (propertize relative-dirs 'face (if relative-faces `(:inherit ,relative-faces))) | |
(propertize (file-name-nondirectory buffer-file-name) | |
'face (if file-faces `(:inherit ,file-faces)))))))) | |
(defmemoize +doom-abbreviate-file-name (file-name) | |
(abbreviate-file-name file-name)) | |
(defmemoize +doom-shrink-path-file-mixed (project-root file-name) | |
(shrink-path-file-mixed project-root | |
(file-name-directory file-name) | |
file-name)) | |
(defun +doom-modeline--buffer-file-name (truncate-project-root-parent) | |
"Propertized `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" | |
(let* ((projectile-require-project-root nil) | |
(project-root (projectile-project-root)) | |
(file-name-split (+doom-shrink-path-file-mixed project-root | |
(or buffer-file-truename | |
(file-truename buffer-file-name))))) | |
(if (null file-name-split) | |
"%b" | |
(pcase-let ((`(,root-path-parent ,project ,relative-path ,filename) file-name-split)) | |
(let ((modified-faces (if (buffer-modified-p) 'doom-modeline-buffer-modified)) | |
(active (active))) | |
(let ((sp-faces (or modified-faces (if active 'font-lock-comment-face))) | |
(project-faces (or modified-faces (if active 'font-lock-string-face))) | |
(relative-faces (or modified-faces (if active 'doom-modeline-buffer-path))) | |
(file-faces (or modified-faces (if active 'doom-modeline-buffer-file)))) | |
(let ((sp-props `(,@(if sp-faces `(:inherit ,sp-faces)) ,@(if active '(:weight bold)))) | |
(project-props `(,@(if project-faces `(:inherit ,project-faces)) ,@(if active '(:weight bold)))) | |
(relative-props `(,@(if relative-faces `(:inherit ,relative-faces)))) | |
(file-props `(,@(if file-faces `(:inherit ,file-faces))))) | |
(concat (propertize (if truncate-project-root-parent | |
root-path-parent | |
(+doom-abbreviate-file-name project-root)) | |
'face sp-props) | |
(propertize (concat project "/") 'face project-props) | |
(when relative-path (propertize relative-path 'face relative-props)) | |
(propertize filename 'face file-props))))))))) | |
;; | |
;; Segments | |
;; | |
(def-modeline-segment! buffer-project | |
"Displays `projectile-project-root'. This is for special buffers like the scratch | |
buffer where knowing the current project directory is important." | |
(when (projectile-project-p) | |
(let ((face (if (active) 'doom-modeline-buffer-path))) | |
(concat (if (display-graphic-p) " ") | |
(all-the-icons-octicon | |
"file-directory" | |
:face face | |
:v-adjust -0.05 | |
:height 1.25) | |
(propertize (concat " " (+doom-abbreviate-file-name (projectile-project-root))) | |
'face face))))) | |
(def-modeline-segment! buffer-project-name | |
"Displays `projectile-project-name'." | |
(when (projectile-project-p) | |
(let ((face (if (active) 'doom-modeline-buffer-path)) | |
(bracket-face (if (active) 'doom-modeline-bracket))) | |
(concat | |
(propertize "{" 'face bracket-face) | |
(propertize (concat (+doom-abbreviate-file-name (projectile-project-name))) | |
'face face) | |
(propertize "} " 'face bracket-face))))) | |
;; | |
(def-modeline-segment! buffer-info | |
"Combined information about the current buffer, including the current working | |
directory, the file name, and its state (modified, read-only or non-existent)." | |
(let ((all-the-icons-scale-factor 1.2)) | |
(concat (if buffer-file-name | |
(+doom-modeline-buffer-file-name) | |
"%b") | |
(cond (buffer-read-only | |
(concat " " | |
(if (display-graphic-p) | |
(all-the-icons-octicon | |
"lock" | |
:face 'doom-modeline-warning | |
:v-adjust -0.05) | |
(propertize "(read-only)" 'face 'doom-modeline-warning)))) | |
((buffer-modified-p) | |
(concat " " | |
(if (display-graphic-p) | |
(all-the-icons-faicon | |
"floppy-o" | |
:face 'doom-modeline-buffer-modified | |
:v-adjust -0.0575) | |
(propertize "(modified)" 'face 'doom-modeline-buffer-modified)))) | |
((and buffer-file-name | |
(not (file-exists-p buffer-file-name))) | |
(concat " " | |
(if (display-graphic-p) | |
(all-the-icons-octicon | |
"circle-slash" | |
:face 'doom-modeline-urgent | |
:v-adjust -0.05) | |
(propertize "(new)" 'face 'doom-modeline-urgent)))) | |
((buffer-narrowed-p) | |
(concat " " | |
(if (display-graphic-p) | |
(all-the-icons-octicon | |
"fold" | |
:face 'doom-modeline-warning | |
:v-adjust -0.05) | |
(propertize "(narrowed)" 'face 'doom-modeline-warning)))))))) | |
;; | |
(def-modeline-segment! buffer-info-simple | |
"Return the current buffer name only, but with fontification." | |
(propertize "%b" 'face (if (active) 'doom-modeline-buffer-file))) | |
;; | |
(def-modeline-segment! buffer-encoding | |
"Displays the encoding and eol style of the buffer the same way Atom does." | |
(concat (pcase (coding-system-eol-type buffer-file-coding-system) | |
(0 "LF ") | |
(1 "CRLF ") | |
(2 "CR ")) | |
(let ((sys (coding-system-plist buffer-file-coding-system))) | |
(cond ((memq (plist-get sys :category) '(coding-category-undecided coding-category-utf-8)) | |
"UTF-8") | |
(t (upcase (symbol-name (plist-get sys :name)))))) | |
" ")) | |
;; | |
(def-modeline-segment! major-mode | |
"The major mode, including process, environment and text-scale info." | |
(propertize | |
(concat (format-mode-line mode-name) | |
(when (stringp mode-line-process) | |
mode-line-process) | |
(when +doom-modeline-env-version | |
(concat " " +doom-modeline-env-version)) | |
(and (featurep 'face-remap) | |
(/= text-scale-mode-amount 0) | |
(format " (%+d)" text-scale-mode-amount))) | |
'face (if (active) 'doom-modeline-buffer-major-mode))) | |
(defun +doom-maybe-icon-octicon (&rest args) | |
(when (display-graphic-p) | |
(apply 'all-the-icons-octicon args))) | |
;; | |
(def-modeline-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)) | |
(face 'mode-line-inactive) | |
(active (active)) | |
(all-the-icons-scale-factor 1.0) | |
(all-the-icons-default-adjust -0.1)) | |
(concat " " | |
(cond ((memq state '(edited added)) | |
(if active (setq face 'doom-modeline-info)) | |
(+doom-maybe-icon-octicon | |
"git-compare" | |
:face face | |
:height 1.2 | |
:v-adjust -0.05)) | |
((eq state 'needs-merge) | |
(if active (setq face 'doom-modeline-info)) | |
(+doom-maybe-icon-octicon "git-merge" :face face)) | |
((eq state 'needs-update) | |
(if active (setq face 'doom-modeline-warning)) | |
(+doom-maybe-icon-octicon "arrow-down" :face face)) | |
((memq state '(removed conflict unregistered)) | |
(if active (setq face 'doom-modeline-urgent)) | |
(+doom-maybe-icon-octicon "alert" :face face)) | |
(t | |
(if active (setq face 'font-lock-doc-face)) | |
(+doom-maybe-icon-octicon | |
"git-compare" | |
:face face | |
:height 1.2 | |
:v-adjust -0.05))) | |
(when (display-graphic-p) " ") | |
(propertize (substring vc-mode (+ (if (eq backend 'Hg) 2 3) 2)) | |
'face (if active face)) | |
" ")))) | |
;; | |
(defun +doom-ml-icon (icon &optional text face voffset) | |
"Displays an octicon ICON with FACE, followed by TEXT. Uses | |
`all-the-icons-octicon' to fetch the icon." | |
(concat (if vc-mode " " " ") | |
(when icon | |
(concat | |
(all-the-icons-material icon :face face :height 1.1 :v-adjust (or voffset -0.2)) | |
(if text +doom-modeline-vspc))) | |
(when text | |
(propertize text 'face face)) | |
(if vc-mode " " " "))) | |
(def-modeline-segment! flycheck | |
"Displays color-coded flycheck error status in the current buffer with pretty | |
icons." | |
(when (boundp 'flycheck-last-status-change) | |
(pcase flycheck-last-status-change | |
('finished (if flycheck-current-errors | |
(let-alist (flycheck-count-errors flycheck-current-errors) | |
(let ((sum (+ (or .error 0) (or .warning 0)))) | |
(+doom-ml-icon "do_not_disturb_alt" | |
(number-to-string sum) | |
(if .error 'doom-modeline-urgent 'doom-modeline-warning) | |
-0.25))) | |
(+doom-ml-icon "check" nil 'doom-modeline-info))) | |
('running (+doom-ml-icon "access_time" nil 'font-lock-doc-face -0.25)) | |
('no-checker (+doom-ml-icon "sim_card_alert" "-" 'font-lock-doc-face)) | |
('errored (+doom-ml-icon "sim_card_alert" "Error" 'doom-modeline-urgent)) | |
('interrupted (+doom-ml-icon "pause" "Interrupted" 'font-lock-doc-face))))) | |
;; ('interrupted (+doom-ml-icon "x" "Interrupted" 'font-lock-doc-face))))) | |
;; | |
(defsubst doom-column (pos) | |
(save-excursion (goto-char pos) | |
(current-column))) | |
(def-modeline-segment! selection-info | |
"Information about the current selection, such as how many characters and | |
lines are selected, or the NxM dimensions of a block selection." | |
(when (and (active) (or mark-active (eq evil-state 'visual))) | |
(let ((reg-beg (region-beginning)) | |
(reg-end (region-end))) | |
(propertize | |
(let ((lines (count-lines reg-beg (min (1+ reg-end) (point-max))))) | |
(cond ((or (bound-and-true-p rectangle-mark-mode) | |
(eq 'block evil-visual-selection)) | |
(let ((cols (abs (- (doom-column reg-end) | |
(doom-column reg-beg))))) | |
(format "%dx%dB" lines cols))) | |
((eq 'line evil-visual-selection) | |
(format "%dL" lines)) | |
((> lines 1) | |
(format "%dC %dL" (- (1+ reg-end) reg-beg) lines)) | |
(t | |
(format "%dC" (- (1+ reg-end) reg-beg))))) | |
'face 'doom-modeline-highlight)))) | |
;; | |
(defun +doom-modeline--macro-recording () | |
"Display current Emacs or evil macro being recorded." | |
(when (and (active) (or defining-kbd-macro executing-kbd-macro)) | |
(let ((sep (propertize " " 'face 'doom-modeline-panel))) | |
(concat sep | |
(propertize (if (bound-and-true-p evil-this-macro) | |
(char-to-string evil-this-macro) | |
"Macro") | |
'face 'doom-modeline-panel) | |
sep | |
(all-the-icons-octicon "triangle-right" | |
:face 'doom-modeline-panel | |
:v-adjust -0.05) | |
sep)))) | |
(defsubst +doom-modeline--anzu () | |
"Show the match index and total number thereof. Requires `anzu', also | |
`evil-anzu' if using `evil-mode' for compatibility with `evil-search'." | |
(when (and anzu--state (not iedit-mode)) | |
(propertize | |
(let ((here anzu--current-position) | |
(total anzu--total-matched)) | |
(cond ((eq anzu--state 'replace-query) | |
(format " %d replace " total)) | |
((eq anzu--state 'replace) | |
(format " %d/%d " here total)) | |
(anzu--overflow-p | |
(format " %s+ " total)) | |
(t | |
(format " %s/%d " here total)))) | |
'face (if (active) 'doom-modeline-panel)))) | |
(defsubst +doom-modeline--evil-substitute () | |
"Show number of matches for evil-ex substitutions and highlights in real time." | |
(when (and 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))) | |
(propertize | |
(let ((range (if evil-ex-range | |
(cons (car evil-ex-range) (cadr evil-ex-range)) | |
(cons (line-beginning-position) (line-end-position)))) | |
(pattern (car-safe (evil-delimited-arguments evil-ex-argument 2)))) | |
(if pattern | |
(format " %s matches " (how-many pattern (car range) (cdr range))) | |
" - ")) | |
'face (if (active) 'doom-modeline-panel)))) | |
(defun doom-themes--overlay-sort (a 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) | |
(propertize | |
(let ((this-oc (or (let ((inhibit-message t)) | |
(iedit-find-current-occurrence-overlay)) | |
(progn (iedit-prev-occurrence) | |
(iedit-find-current-occurrence-overlay)))) | |
(length (length iedit-occurrences-overlays))) | |
(format " %s/%d " | |
(if this-oc | |
(- length | |
(length (memq this-oc (sort (append iedit-occurrences-overlays nil) | |
#'doom-themes--overlay-sort))) | |
-1) | |
"-") | |
length)) | |
'face (if (active) 'doom-modeline-panel)))) | |
(def-modeline-segment! matches | |
"Displays: 1. the currently recording macro, 2. A current/total for the | |
current search term (with anzu), 3. The number of substitutions being conducted | |
with `evil-ex-substitute', and/or 4. The number of active `iedit' regions." | |
(let ((meta (concat (+doom-modeline--macro-recording) | |
(+doom-modeline--anzu) | |
(+doom-modeline--evil-substitute) | |
(+doom-modeline--iedit)))) | |
(concat | |
(unless (equal meta "") meta) | |
" "))) | |
;; TODO Include other information | |
(def-modeline-segment! media-info | |
"Metadata regarding the current file, such as dimensions for images." | |
(cond ((eq major-mode 'image-mode) | |
(cl-destructuring-bind (width . height) | |
(image-size (image-get-display-property) :pixels) | |
(format " %dx%d " width height))))) | |
(def-modeline-segment! bar | |
"The bar regulates the height of the mode-line in GUI Emacs. | |
Returns \"\" to not break --no-window-system." | |
(if (display-graphic-p) | |
(+doom-modeline--make-xpm | |
(face-background (if (active) | |
(spacemacs//evil-state-face) | |
'doom-modeline-inactive-bar) | |
nil t) | |
+doom-modeline-height | |
+doom-modeline-bar-width) | |
"")) | |
(defun +doom-modeline-eyebrowse-number () | |
(when (and (bound-and-true-p eyebrowse-mode) | |
(< 1 (length (eyebrowse--get 'window-configs)))) | |
(let* ((num (eyebrowse--get 'current-slot)) | |
(tag (when num (nth 2 (assoc num (eyebrowse--get 'window-configs))))) | |
(str (if (and tag (< 0 (length tag))) | |
tag | |
(when num (int-to-string num))))) | |
str))) | |
(defun +doom-window-bottom-left-p () | |
(let* ((edges (window-edges)) | |
(minibuffer-edges (window-edges (minibuffer-window)))) | |
(and (eq 0 (car edges)) | |
(eq (nth 3 edges) | |
(cadr minibuffer-edges))))) | |
(def-modeline-segment! persp-number | |
"The persp number." | |
(when (+doom-window-bottom-left-p) | |
(when-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)))) | |
;; | |
;; Mode lines | |
;; | |
(def-modeline! main | |
(bar matches persp-number buffer-project-name buffer-info " %l:%c %p " selection-info) | |
(major-mode vcs flycheck)) | |
(def-modeline! minimal | |
(bar matches " " buffer-info) | |
(media-info major-mode)) | |
(def-modeline! special | |
(bar matches " " buffer-info-simple " %l:%c %p " selection-info) | |
(major-mode flycheck)) | |
(def-modeline! project | |
(bar buffer-project) | |
(major-mode)) | |
(def-modeline! media | |
(bar " %b ") | |
(media-info major-mode)) | |
;; | |
;; Hooks | |
;; | |
(defun +doom-modeline|init () | |
"Set the default modeline." | |
(doom-set-modeline 'main t) | |
;; This scratch and messages buffer is already created and doesn't get a | |
;; modeline. | |
(with-current-buffer "*Messages*" | |
(doom-set-modeline 'main)) | |
(with-current-buffer "*scratch*" | |
(doom-set-modeline 'main))) | |
(defun +doom-modeline|set-special-modeline () | |
(doom-set-modeline 'special)) | |
(defun +doom-modeline|set-media-modeline () | |
(doom-set-modeline 'media)) | |
;; | |
;; Bootstrap | |
;; | |
(add-hook 'org-src-mode-hook #'+doom-modeline|set-special-modeline) | |
(add-hook 'image-mode-hook #'+doom-modeline|set-media-modeline) | |
(add-hook 'circe-mode-hook #'+doom-modeline|set-special-modeline) | |
(provide 'doom-modeline) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
(defconst doom-modeline-packages | |
'(all-the-icons | |
(doom-modeline :location local))) | |
(defun doom-modeline/init-all-the-icons () | |
(use-package all-the-icons | |
:defer t)) | |
(defun doom-modeline/init-doom-modeline () | |
(use-package doom-modeline | |
:demand t | |
:init | |
(setq +doom-modeline-buffer-file-name-style 'relative-to-project | |
+doom-modeline-height 26 | |
+doom-modeline-bar-width 3) | |
:config | |
(+doom-modeline|init))) |
@jbharat no problem. Apparently I don't get notifications here, sorry. What did you do to get evil state change? I've thought about adding that but haven't had a chance.
I have the spaceline
package excluded, you may just need to (setq powerline-image-apple-rgb t)
or nil
if you're not on a mac.
Figured it out. I updated the gist.
Thanks. I have updated it already as per my needs. I am putting the link to the bug here as a reference.
syl20bnr/spacemacs#10350
Thanks for sharing. It works fine but the evil state change doesn't work on pdump spacemacs.
layer dir looks like this:
doom-modeline
├── local
│ └── doom-modeline
│ └── doom-modeline.el
└── packages.el
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Thanks, @aaronjensen I got it working. I do get an error as
Error during redisplay: (eval (doom-modeline-format--main)) signaled (void-variable powerline-image-apple-rgb) [x times]
if I don't put the below code.Empty mode-line is rendered with this error. I am not sure. Maybe it is because I use emacs(27.0.50 and spacemacs develop branch) in daemon mode.
I also had to add
shrink-path
to additional packages.I don't have much lisp coding exp.
I want to know if there a way to get the doom-modeline-bar(the tiny bar at the start of modeline) to change colour when in insert/normal/visual modes?Great work. Thanks again
EDIT: With help from Alexander-Miller, I got evil state change in doom-modeline-bar