Adopt seagle0128/doom-modeline for :ui modeline

And remove obsolete :ui doom-modeline module.

Relevant to: #136, #921
This commit is contained in:
Henrik Lissner 2019-03-01 15:12:27 -05:00
parent 868bd15abe
commit 8832737671
No known key found for this signature in database
GPG key ID: 5F6C0EA160557395
17 changed files with 90 additions and 1944 deletions

View file

@ -17,7 +17,8 @@
(syntax-checker (:tools flycheck)))
(:tools (rotate-text (:editor rotate-text)))
(:emacs (electric-indent (:emacs electric))
(hideshow (:editor fold))))
(hideshow (:editor fold)))
(:ui (doom-modeline (:ui modeline))))
"An alist of deprecated modules, mapping deprecated modules to an optional new
location (which will create an alias). Each CAR and CDR is a (CATEGORY .
MODULES). E.g.

View file

@ -21,12 +21,11 @@
;;deft ; notational velocity for Emacs
doom ; what makes DOOM look the way it does
doom-dashboard ; a nifty splash screen for Emacs
doom-modeline ; a snazzy Atom-inspired mode-line
doom-quit ; DOOM quit-message prompts when you quit Emacs
evil-goggles ; display visual hints when editing in evil
;;fci ; a `fill-column' indicator
hl-todo ; highlight TODO/FIXME/NOTE tags
;;modeline ; snazzy, Atom-inspired modeline, plus API
modeline ; snazzy, Atom-inspired modeline, plus API
nav-flash ; blink the current line after jumping
;;neotree ; a project drawer, like NERDTree for vim
treemacs ; a project drawer, like neotree but cooler

View file

@ -1,8 +1,5 @@
;;; lang/python/autoload/python.el -*- lexical-binding: t; -*-
(defvar +python-version-cache (make-hash-table :test 'equal)
"TODO")
;;;###autoload
(defun +python/open-repl ()
"Open the Python REPL."
@ -38,53 +35,3 @@
(let ((python-shell-interpreter "jupyter")
(python-shell-interpreter-args (format "console %s" (string-join +python-jupyter-repl-args " "))))
(+python/open-repl)))
(defun +python--extract-version (prefix str)
(when str
(format "%s%s" prefix (cadr (split-string str " ")))))
;;;###autoload
(defun +python-version ()
"Return the currently installed version of python on your system or active in
the current pipenv.
This is not necessarily aware of env management tools like virtualenv, pyenv or
pipenv, unless those tools have modified the PATH that Emacs picked up when you
started it."
(condition-case _
(if-let* ((proot (and (fboundp 'pipenv-project-p)
(pipenv-project-p))))
(let* ((default-directory proot)
(v (car (process-lines "pipenv" "run" "python" "--version"))))
(puthash proot
(+python--extract-version "Pipenv " v)
+python-version-cache))
(puthash (or (doom-project-root) default-directory)
(+python--extract-version
"Python "
(car (process-lines python-shell-interpreter "--version")))
+python-version-cache))
(error "Python")))
;;
;; Hooks
;;;###autoload
(defun +python|update-version (&rest _)
"Update `+python--version' by consulting `+python-version' function."
(setq +python--version
(or (gethash (or (and (fboundp 'pipenv-project-p)
(pipenv-project-p))
(doom-project-root)
default-directory)
+python-version-cache)
(+python-version))))
;;;###autoload
(defun +python|update-version-in-all-buffers (&rest _)
"Update `+python-version' in all buffers in `python-mode'."
(dolist (buffer (doom-buffers-in-mode 'python-mode))
(setq +python-version-cache (clrhash +python-version-cache))
(with-current-buffer buffer
(+python|update-version))))

View file

@ -11,9 +11,6 @@ called.")
"CLI arguments to initialize 'jupiter console %s' with when
`+python/open-ipython-repl' is called.")
(defvar-local +python--version nil
"The python version in the current buffer.")
;;
;; Packages
@ -55,14 +52,7 @@ called.")
sp-point-after-word-p
sp-point-before-same-p))
(setq-hook! 'python-mode-hook tab-width python-indent-offset)
;; Add python/pipenv version string to the major mode in the modeline
(defun +python|init-mode-line ()
(setq mode-name +python-mode-line-indicator))
(add-hook 'python-mode-hook #'+python|init-mode-line)
(add-hook 'python-mode-hook #'+python|update-version))
(setq-hook! 'python-mode-hook tab-width python-indent-offset))
(def-package! anaconda-mode
@ -152,9 +142,9 @@ called.")
(format "PIPENV_MAX_DEPTH=9999 %s run %%c %%o %%s %%a" bin)
"%c %o %s %a")))
(:description . "Run Python script")))
(advice-add #'pipenv-activate :after-while #'+python|update-version-in-all-buffers)
(advice-add #'pipenv-deactivate :after-while #'+python|update-version-in-all-buffers))
(when (featurep! :ui modeline)
(advice-add #'pipenv-activate :after-while #'+modeline|update-env-in-all-windows)
(advice-add #'pipenv-deactivate :after-while #'+modeline|update-env-in-all-windows)))
(def-package! pyenv-mode
@ -164,8 +154,9 @@ called.")
(pyenv-mode +1)
(when (executable-find "pyenv")
(add-to-list 'exec-path (expand-file-name "shims" (or (getenv "PYENV_ROOT") "~/.pyenv"))))
(advice-add #'pyenv-mode-set :after #'+python|update-version-in-all-buffers)
(advice-add #'pyenv-mode-unset :after #'+python|update-version-in-all-buffers))
(when (featurep! :ui modeline)
(advice-add #'pyenv-mode-set :after #'+modeline|update-env-in-all-windows)
(advice-add #'pyenv-mode-unset :after #'+modeline|update-env-in-all-windows)))
(def-package! pyvenv
@ -173,9 +164,10 @@ called.")
:after python
:config
(defun +python-current-pyvenv () pyvenv-virtual-env-name)
(add-hook 'pyvenv-post-activate-hooks #'+python|update-version-in-all-buffers)
(add-hook 'pyvenv-post-deactivate-hooks #'+python|update-version-in-all-buffers)
(add-to-list '+python-mode-line-indicator
(when (featurep! :ui modeline)
(add-hook 'pyvenv-post-activate-hooks #'+modeline|update-env-in-all-windows)
(add-hook 'pyvenv-post-deactivate-hooks #'+modeline|update-env-in-all-windows))
(add-to-list 'global-mode-string
'(pyvenv-virtual-env-name (" venv:" pyvenv-virtual-env-name))
'append))
@ -214,8 +206,9 @@ called.")
(conda-env-initialize-interactive-shells)
(after! eshell (conda-env-initialize-eshell))
(add-hook 'conda-postactivate-hook #'+python|update-version-in-all-buffers)
(add-hook 'conda-postdeactivate-hook #'+python|update-version-in-all-buffers)
(add-to-list '+python-mode-line-indicator
(when (featurep! :ui modeline)
(add-hook 'conda-postactivate-hook #'+modeline|update-env-in-all-windows)
(add-hook 'conda-postdeactivate-hook #'+modeline|update-env-in-all-windows))
(add-to-list 'global-mode-string
'(conda-env-current-name (" conda:" conda-env-current-name))
'append))

View file

@ -1,8 +1,5 @@
;;; lang/ruby/autoload.el -*- lexical-binding: t; -*-
(defvar +ruby-version-cache (make-hash-table :test 'equal)
"TODO")
;;;###autoload
(defun +ruby|cleanup-robe-servers ()
"Clean up dangling inf robe processes if there are no more `enh-ruby-mode'
@ -16,38 +13,3 @@ buffers open."
(when (processp process)
(kill-process (get-buffer-process inf-buffer))
(kill-buffer inf-buffer)))))))
;;;###autoload
(defun +ruby-version ()
"Return the currently installed version of ruby on your system (the first
ruby executable found in your PATH).
This is not necessarily aware of env management tools like virtualenv, pyenv or
pipenv, unless those tools have modified the PATH that Emacs picked up when you
started it."
(condition-case _
(let ((version-str (car (process-lines "ruby" "--version"))))
(puthash (or (doom-project-root) default-directory)
(format "Ruby %s" (cadr (split-string version-str " ")))
+ruby-version-cache))
(error "Ruby")))
;;
;; Hooks
;;;###autoload
(defun +ruby|update-version (&rest _)
"Update `+ruby--version' by consulting `+ruby-version' function."
(setq +ruby--version
(or (gethash (or (doom-project-root) default-directory)
+ruby-version-cache)
(+ruby-version))))
;;;###autoload
(defun +ruby|update-version-in-all-buffers (&rest _)
"Update `+ruby--version' in all `enh-ruby-mode' buffers."
(dolist (buffer (doom-buffers-in-mode 'enh-ruby-mode))
(setq +ruby-version-cache (clrhash +ruby-version-cache))
(with-current-buffer buffer
(+ruby|update-version))))

View file

@ -1,12 +1,5 @@
;;; lang/ruby/config.el -*- lexical-binding: t; -*-
(defvar +ruby-mode-line-indicator '("" +ruby--version)
"Format for the ruby version/env indicator in the mode-line.")
(defvar-local +ruby--version nil
"The ruby version in the current buffer.")
;;
;; Packages
@ -34,14 +27,7 @@
(add-to-list 'company-dabbrev-code-modes 'ruby-mode nil #'eq))
;; so class and module pairs work
(setq-hook! (ruby-mode enh-ruby-mode) sp-max-pair-length 6)
;; Add ruby version string to the major mode in the modeline
(defun +ruby|init-mode-line ()
(setq mode-name +ruby-mode-line-indicator))
(add-hook 'enh-ruby-mode-hook #'+ruby|init-mode-line)
(add-hook 'enh-ruby-mode-hook #'+ruby|update-version))
(setq-hook! (ruby-mode enh-ruby-mode) sp-max-pair-length 6))
(def-package! robe

View file

@ -1,20 +0,0 @@
;;; tools/pdf/+modeline.el -*- lexical-binding: t; -*-
(def-modeline-segment! +pdf-pages
"Current and total page indicator for PDF documents."
(format "P %d/%d" (pdf-view-current-page) (pdf-cache-number-of-pages)))
(if (featurep! :ui modeline)
(def-modeline-format! '+pdf
'(+modeline-matches " " +modeline-buffer-id " " +pdf-pages)
'(+modeline-major-mode (vc-mode (" " +modeline-vcs))))
(def-modeline! '+pdf
'(bar matches " " buffer-info " " +pdf-pages)
'(major-mode vcs)))
(defun +pdf|init-modeline ()
(funcall (if (featurep! :ui modeline)
#'set-modeline!
#'doom-set-modeline)
'+pdf))
(add-hook 'pdf-tools-enabled-hook #'+pdf|init-modeline)

View file

@ -28,9 +28,6 @@
(setq-default pdf-view-display-size 'fit-page)
;; Turn off cua so copy works
(add-hook! 'pdf-view-mode-hook (cua-mode 0))
;; Custom modeline that removes useless info and adds page numbers
(when (or (featurep! :ui doom-modeline) (featurep! :ui modeline))
(load! "+modeline"))
;; Handle PDF-tools related popups better
(set-popup-rule! "^\\*Outline*" :side 'right :size 40 :select nil)
;; The next rules are not needed, they are defined in modules/ui/popups/+hacks.el

View file

@ -1,52 +0,0 @@
#+TITLE: :ui doom-modeline
This module customizes the Emacs mode-line.
The DOOM modeline was designed for minimalism, and offers:
+ A match count panel (for ~evil-search~, ~iedit~ and ~evil-substitute~)
+ An indicator for recording a macro
+ Local python/ruby version in the major-mode
+ A customizable mode-line height (see ~+doom-modeline-height~)
+ An error/warning count segment for flycheck
[[/../screenshots/ml.png]]
[[/../screenshots/ml-search.png]]
[[/../screenshots/ml-subst.png]]
[[/../screenshots/ml-macro.png]]
[[/../screenshots/ml-version.png]]
[[/../screenshots/ml-errors.png]]
* Table of Contents :TOC:
- [[#install][Install]]
- [[#extracting-my-modeline][Extracting my modeline]]
- [[#troubleshooting][Troubleshooting]]
- [[#where-are-my-minor-modes][Where are my minor modes?]]
* Install
This module requires the fonts included with ~all-the-icons~ to be installed.
Run ~M-x all-the-icons-install-fonts~ to do so.
* Extracting my modeline
Some might want my modeline without the DOOM config altogether. I've tried to make this easier for you, but there are a few things you'll need to do:
+ Ensure [[https://github.com/bbatsov/projectile][projectile]] and [[https://github.com/domtronn/all-the-icons.el][all-the-icons]] are installed.
+ Ensure ~projectile-mode~ is enabled.
+ Ensure the fonts included with ~all-the-icons~ are installed (~M-x all-the-icons-install-fonts~).
+ Replace ~def-package!~ calls with ~use-package~.
+ Replace ~doom-project-root~ calls with ~projectile-project-root~.
+ The ~+doom-modeline--make-xpm~ function is memoized with the ~def-memoized!~ macro. Change ~def-memoized!~ to ~defun~.
+ Copy the ~add-hook!~ macro definition from [[/core/core-lib.el][core/core-lib.el]].
+ Copy the following macros and functions from [[/core/core-ui.el][core/core-ui.el]]:
+ ~def-modeline-segment!~
+ ~def-modeline!~
+ ~doom--prepare-modeline-segments~
+ ~doom-modeline~
+ ~doom-set-modeline~
That /should/ be everything. As I have never used this out of my config I can't guarantee immediate success, but I'd be happy to help you out if you file an issue.
* Troubleshooting
** Where are my minor modes?
I didn't need it, so I removed it. Run ~M-x doom/what-minor-mode~ to investigate what minor modes are currently active.

View file

@ -1,19 +0,0 @@
;;; ui/doom-modeline/autoload.el -*- lexical-binding: t; -*-
(defvar +doom-modeline--old-bar-height nil)
;;;###autoload
(defun +doom-modeline|resize-for-big-font ()
"Adjust the modeline's height when `doom-big-font-mode' is enabled. This was
made to be added to `doom-big-font-mode-hook'."
(unless +doom-modeline--old-bar-height
(setq +doom-modeline--old-bar-height +doom-modeline-height))
(let ((default-height +doom-modeline--old-bar-height))
(if doom-big-font-mode
(let* ((font-size (font-get doom-font :size))
(big-size (font-get doom-big-font :size))
(ratio (/ (float big-size) font-size)))
(setq +doom-modeline-height (ceiling (* default-height ratio 0.75))))
(setq +doom-modeline-height default-height))
;; already has a variable watcher in Emacs 26+
(unless EMACS26+ (+doom-modeline|refresh-bars))))

View file

@ -1,830 +0,0 @@
;;; ui/doom-modeline/config.el -*- lexical-binding: t; -*-
;; We handle this ourselves
(setq projectile-dynamic-mode-line nil)
;;
;; Modeline library
(defvar doom--modeline-fn-alist ())
(defvar doom--modeline-var-alist ())
(defmacro def-modeline-segment! (name &rest body)
"Defines a modeline segment and byte compiles it."
(declare (indent defun) (doc-string 2))
(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
(fset ',sym (lambda () ,docstring ,@body))
(add-to-list 'doom--modeline-fn-alist (cons ',name ',sym))
,(unless (bound-and-true-p byte-compile-current-file)
`(let (byte-compile-warnings)
(byte-compile #',sym))))))))
(defun doom--prepare-modeline-segments (segments)
(let (forms it)
(dolist (seg segments)
(cond ((stringp seg)
(push seg forms))
((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)))
(defun 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)))
(defalias sym
(lambda ()
(let ((lhs (eval `(list ,@lhs-forms) t))
(rhs (eval `(list ,@rhs-forms) t)))
(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))))
(concat "Modeline:\n"
(format " %s\n %s"
(prin1-to-string lhs)
(prin1-to-string rhs))))))
(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-soft (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)))
(list "%e" modeline))))
;;
;; Custom faces
(defgroup +doom-modeline nil
"TODO"
: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)
(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)
(defface doom-modeline-buffer-modified
'((t (:inherit (error bold) :background nil)))
"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))))
"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))))
"Face for info-level messages in the modeline. Used by `*vc'."
:group '+doom-modeline)
(defface doom-modeline-warning
`((t (:inherit (warning bold))))
"Face for warnings in the modeline. Used by `*flycheck'"
:group '+doom-modeline)
(defface doom-modeline-urgent
`((t (:inherit (error bold))))
"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)
;;
;; Packages
;; anzu and evil-anzu expose current/total state that can be displayed in the
;; mode-line.
(def-package! anzu
:after-call isearch-mode
:config
(setq anzu-cons-mode-line-p nil
anzu-minimum-input-length 1
anzu-search-threshold 250)
(global-anzu-mode +1)
(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
(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 'doom-escape-hook #'anzu--reset-status t)
(add-hook 'iedit-mode-end-hook #'anzu--reset-status))
(def-package! evil-anzu
:when (featurep! :feature evil)
:after-call (evil-ex-start-search evil-ex-start-word-search))
;; fish-style modeline
(def-package! shrink-path
:commands (shrink-path-prompt shrink-path-file-mixed))
;; 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"
(when-let* ((win (frame-selected-window)))
(unless (minibuffer-window-active-p win)
(setq +doom-modeline-current-window win)
(force-mode-line-update))))
(defun +doom-modeline|unset-selected-window ()
(setq +doom-modeline-current-window nil)
(force-mode-line-update))
(add-hook 'window-configuration-change-hook #'+doom-modeline|set-selected-window)
(add-hook 'doom-enter-window-hook #'+doom-modeline|set-selected-window)
(with-no-warnings
(cond ((not (boundp 'after-focus-change-function))
(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))))
;;
;; Variables
(defvar +doom-modeline-height 23
"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-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")
;; 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)
;;
;; Modeline helpers
(defun active ()
(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'."
(propertize
" " 'display
(let ((data (make-list height (make-list width 1)))
(color (or (face-background face nil t) "None")))
(ignore-errors
(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'."
(let ((buffer-file-name (or (buffer-file-name (buffer-base-buffer)) "")))
(unless buffer-file-truename
(setq buffer-file-truename (file-truename buffer-file-name)))
(propertize
(pcase +doom-modeline-buffer-file-name-style
(`truncate-upto-project
(+doom-modeline--buffer-file-name buffer-file-name buffer-file-truename 'shrink))
(`truncate-upto-root
(+doom-modeline--buffer-file-name-truncate buffer-file-name buffer-file-truename))
(`truncate-all
(+doom-modeline--buffer-file-name-truncate buffer-file-name buffer-file-truename t))
(`relative-to-project
(+doom-modeline--buffer-file-name-relative buffer-file-name buffer-file-truename))
(`relative-from-project
(+doom-modeline--buffer-file-name-relative buffer-file-name buffer-file-truename 'include-project))
(`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 buffer-file-truename)))
(defun +doom-modeline--buffer-file-name-truncate (file-path true-file-path &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 true-file-path)))
(active (active)))
(if (null dirs)
(propertize "%b" 'face (if active 'doom-modeline-buffer-file))
(let ((modified-faces (if (buffer-modified-p) 'doom-modeline-buffer-modified)))
(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 (concat dirname
(if truncate-tail (substring basename 0 1) basename)
"/")
'face (if dir-faces `(:inherit ,dir-faces)))
(propertize (file-name-nondirectory file-path)
'face (if file-faces `(:inherit ,file-faces)))))))))
(defun +doom-modeline--buffer-file-name-relative (_file-path true-file-path &optional include-project)
"Propertized `buffer-file-name' showing directories relative to project's root only."
(let ((root (or (doom-project-root) default-directory))
(active (active)))
(if (null root)
(propertize "%b" 'face (if active 'doom-modeline-buffer-file))
(let* ((modified-faces (if (buffer-modified-p) 'doom-modeline-buffer-modified))
(relative-dirs (file-relative-name (file-name-directory true-file-path)
(if include-project (concat root "../") 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 true-file-path)
'face (if file-faces `(:inherit ,file-faces))))))))
(defun +doom-modeline--buffer-file-name (file-path _true-file-path &optional 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* ((project-root (or (doom-project-root) default-directory))
(file-name-split (shrink-path-file-mixed project-root
(file-name-directory file-path)
file-path))
(active (active)))
(if (null file-name-split)
(propertize "%b" 'face (if active 'doom-modeline-buffer-file))
(pcase-let ((`(,root-path-parent ,project ,relative-path ,file-path) file-name-split))
(let ((modified-faces (if (buffer-modified-p) 'doom-modeline-buffer-modified)))
(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
(abbreviate-file-name project-root))
'face sp-props)
(propertize (concat project "/") 'face project-props)
(if relative-path (propertize relative-path 'face relative-props))
(propertize file-path 'face file-props)))))))))
;;
;; buffer information
(def-modeline-segment! buffer-default-directory
"Displays `default-directory'. This is for special buffers like the scratch
buffer where knowing the current project directory is important."
(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 " " (abbreviate-file-name default-directory))
'face 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)."
(concat (cond (buffer-read-only
(concat (all-the-icons-octicon
"lock"
:face 'doom-modeline-warning
:v-adjust -0.05)
" "))
((buffer-modified-p)
(concat (all-the-icons-faicon
"floppy-o"
:face 'doom-modeline-buffer-modified
:v-adjust -0.0575)
" "))
((and buffer-file-name
(not (file-exists-p buffer-file-name)))
(concat (all-the-icons-octicon
"circle-slash"
:face 'doom-modeline-urgent
:v-adjust -0.05)
" "))
((buffer-narrowed-p)
(concat (all-the-icons-octicon
"fold"
:face 'doom-modeline-warning
:v-adjust -0.05)
" ")))
(if buffer-file-name
(+doom-modeline-buffer-file-name)
"%b")))
(def-modeline-segment! buffer-info-simple
"Display only the current buffer's name, but with fontification."
(propertize
"%b"
'face (cond ((and buffer-file-name (buffer-modified-p))
'doom-modeline-buffer-modified)
((active) 'doom-modeline-buffer-file))))
;; (defvar +doom-modeline--encoding nil)
;; (def-modeline-segment! buffer-encoding
;; "TODO"
;; +doom-modeline--encoding)
;; (add-variable-watcher
;; 'buffer-file-coding-system
;; (lambda (_sym val op _where)
;; (when (eq op 'set)
;; (setq +doom-modeline--encoding
;; (concat (pcase (coding-system-eol-type val)
;; (0 "LF ")
;; (1 "CRLF ")
;; (2 "CR "))
;; (let ((sys (coding-system-plist val)))
;; (if (memq (plist-get sys :category) '(coding-category-undecided coding-category-utf-8))
;; "UTF-8"
;; (upcase (symbol-name (plist-get sys :name)))))
;; " ")))))
(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))))))
" "))
;;
;; major-mode
(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)
(and (boundp 'text-scale-mode-amount)
(/= text-scale-mode-amount 0)
(format " (%+d)" text-scale-mode-amount)))
'face (if (active) 'doom-modeline-buffer-major-mode)))
;;
;; vcs
(defvar-local +doom-modeline--vcs nil)
(defun +doom-modeline--update-vcs ()
(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 (active))
(all-the-icons-default-adjust -0.1))
(concat " "
(cond ((memq state '(edited added))
(if active (setq face 'doom-modeline-info))
(all-the-icons-octicon
"git-compare"
:face face
:v-adjust -0.05))
((eq state 'needs-merge)
(if active (setq face 'doom-modeline-info))
(all-the-icons-octicon "git-merge" :face face))
((eq state 'needs-update)
(if active (setq face 'doom-modeline-warning))
(all-the-icons-octicon "arrow-down" :face face))
((memq state '(removed conflict unregistered))
(if active (setq face 'doom-modeline-urgent))
(all-the-icons-octicon "alert" :face face))
(t
(if active (setq face 'font-lock-doc-face))
(all-the-icons-octicon
"git-compare"
:face face
:v-adjust -0.05)))
" "
(propertize (substring vc-mode (+ (if (eq backend 'Hg) 2 3) 2))
'face (if active face))
" "))))))
(add-hook 'after-revert-hook #'+doom-modeline--update-vcs)
(add-hook 'after-save-hook #'+doom-modeline--update-vcs)
(add-hook 'find-file-hook #'+doom-modeline--update-vcs t)
(advice-add #'vc-refresh-state :after #'+doom-modeline--update-vcs)
(def-modeline-segment! vcs
"Displays the current branch, colored based on its state."
+doom-modeline--vcs)
;;
;; flycheck
(defvar +doom-modeline-vspc
(propertize " " 'face 'variable-pitch)
"TODO")
(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)))
(if text (propertize text 'face face))
(if vc-mode " " " ")))
(defvar-local +doom-modeline--flycheck nil)
(add-hook 'flycheck-status-changed-functions #'+doom-modeline|update-flycheck-segment)
(add-hook 'flycheck-mode-hook #'+doom-modeline|update-flycheck-segment)
(defun +doom-modeline|update-flycheck-segment (&optional status)
(setq +doom-modeline--flycheck
(pcase status
('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)))))
(def-modeline-segment! flycheck
"Displays color-coded flycheck error status in the current buffer with pretty
icons."
+doom-modeline--flycheck)
;;
;; selection-info
(defsubst doom-column (pos)
(save-excursion (goto-char pos)
(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.")
(defun +doom-modeline|enable-word-count () (setq +doom-modeline-enable-word-count t))
(add-hook 'text-mode-hook #'+doom-modeline|enable-word-count)
(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)))
(cl-destructuring-bind (beg . end)
(if (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))
(let ((cols (abs (- (doom-column end)
(doom-column beg)))))
(format "%dx%dB" lines cols)))
((eq evil-visual-selection 'line)
(format "%dL" lines))
((> lines 1)
(format "%dC %dL" (- end beg) lines))
((format "%dC" (- end beg))))
(when +doom-modeline-enable-word-count
(format " %dW" (count-words beg end)))))
'face 'doom-modeline-highlight))))
;;
;; matches (anzu, evil-substitute, iedit, macro)
(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))))
(or (and (not (equal meta "")) meta)
(if buffer-file-name " %I "))))
;;
;; media-info
(def-modeline-segment! media-info
"Metadata regarding the current file, such as dimensions for images."
;; TODO Include other information
(cond ((eq major-mode 'image-mode)
(cl-destructuring-bind (width . height)
(image-size (image-get-display-property) :pixels)
(format " %dx%d " width height)))))
;;
;; bar
(defvar +doom-modeline--bar-active nil)
(defvar +doom-modeline--bar-inactive nil)
(def-modeline-segment! bar
"The bar regulates the height of the mode-line in GUI Emacs.
Returns \"\" to not break --no-window-system."
(if window-system
(if (active)
+doom-modeline--bar-active
+doom-modeline--bar-inactive)
""))
(when EMACS26+
(add-variable-watcher
'+doom-modeline-height
(lambda (_sym val op _where)
(when (and (eq op 'set) (integerp val))
(+doom-modeline|refresh-bars +doom-modeline-bar-width val))))
(add-variable-watcher
'+doom-modeline-bar-width
(lambda (_sym val op _where)
(when (and (eq op 'set) (integerp val))
(+doom-modeline|refresh-bars val +doom-modeline-height))))
(add-hook 'doom-big-font-mode-hook #'+doom-modeline|resize-for-big-font))
;;
;; Mode lines
(def-modeline! 'main
'(bar matches " " buffer-info " %l:%c %p " selection-info)
'(buffer-encoding 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)
'(buffer-encoding major-mode flycheck))
(def-modeline! 'project
'(bar buffer-default-directory)
'(major-mode))
(def-modeline! 'media
'(bar " %b ")
'(media-info major-mode))
;;
;; Hooks
(defun +doom-modeline|refresh-bars (&optional width 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))))
(defun +doom-modeline|init ()
;; 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-set-modeline 'main)))))
(defun +doom-modeline|set-special-modeline ()
(doom-set-modeline 'special))
(defun +doom-modeline|set-media-modeline ()
(doom-set-modeline 'media))
(defun +doom-modeline|set-project-modeline ()
(doom-set-modeline 'project))
;;
;; Bootstrap
(doom-set-modeline 'main t) ; set default modeline
(add-hook 'doom-load-theme-hook #'+doom-modeline|init)
(add-hook 'doom-scratch-buffer-hook #'+doom-modeline|set-special-modeline)
(add-hook '+doom-dashboard-mode-hook #'+doom-modeline|set-project-modeline)
(add-hook 'image-mode-hook #'+doom-modeline|set-media-modeline)
(add-hook 'circe-mode-hook #'+doom-modeline|set-special-modeline)
;; Ensure modeline is inactive when Emacs is unfocused (and active otherwise)
(defvar +doom-modeline-remap-face-cookie nil)
(defun +doom-modeline|focus ()
(when +doom-modeline-remap-face-cookie
(require 'face-remap)
(face-remap-remove-relative +doom-modeline-remap-face-cookie)))
(defun +doom-modeline|unfocus ()
(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)

View file

@ -1,20 +0,0 @@
;; -*- no-byte-compile: t; -*-
;;; ui/doom-modeline/packages.el
;;; These are the invisible dependencies
;; Required
;;(require 'evil)
;;(require 'projectile)
;;(require 'all-the-icons)
;; Optional
;;(require 'flycheck)
;;(require 'iedit)
;;(require 'evil-multiedit)
(package! anzu)
(when (featurep! :feature evil)
(package! evil-anzu))
(package! shrink-path)

View file

@ -0,0 +1,35 @@
;;; ui/modeline/autoload/modeline.el -*- lexical-binding: t; -*-
;;;###autodef
(defalias 'def-modeline-format! 'doom-modeline-def-segment)
;;;###autodef
(defalias 'def-modeline-segment! 'doom-modeline-def-modeline)
;;;###autodef
(defalias 'set-modeline! 'doom-modeline-set-modeline)
(defvar +modeline--old-bar-height nil)
;;;###autoload
(defun +modeline|resize-for-big-font ()
"Adjust the modeline's height when `doom-big-font-mode' is enabled. This was
made to be added to `doom-big-font-mode-hook'."
(unless +modeline--old-bar-height
(setq +modeline--old-bar-height doom-modeline-height))
(let ((default-height +modeline--old-bar-height))
(if doom-big-font-mode
(let* ((font-size (font-get doom-font :size))
(big-size (font-get doom-big-font :size))
(ratio (/ (float big-size) font-size)))
(setq doom-modeline-height (ceiling (* default-height ratio 0.75))))
(setq doom-modeline-height default-height))
;; already has a variable watcher in Emacs 26+
(unless EMACS26+ (doom-modeline-refresh-bars))))
;;;###autoload
(defun +modeline|update-env-in-all-windows (&rest _)
""
(dolist (window (window-list))
(with-selected-window window
(doom-modeline-update-env))))

View file

@ -1,19 +0,0 @@
;;; ui/modeline/autoload/modeline.el -*- lexical-binding: t; -*-
;; (defvar +modeline--old-bar-height nil)
;; ;;;###autoload
;; (defun +modeline|resize-for-big-font ()
;; "Adjust the modeline's height when `doom-big-font-mode' is enabled. This was
;; made to be added to `doom-big-font-mode-hook'."
;; (unless +modeline--old-bar-height
;; (setq +modeline--old-bar-height +doom-modeline-height))
;; (let ((default-height +modeline--old-bar-height))
;; (if doom-big-font-mode
;; (let* ((font-size (font-get doom-font :size))
;; (big-size (font-get doom-big-font :size))
;; (ratio (/ (float big-size) font-size)))
;; (setq +doom-modeline-height (ceiling (* default-height ratio 0.75))))
;; (setq +doom-modeline-height default-height))
;; ;; already has a variable watcher in Emacs 26+
;; (unless EMACS26+ (+doom-modeline|refresh-bars))))

View file

@ -1,104 +0,0 @@
;;; ui/modeline/autoload/settings.el -*- lexical-binding: t; -*-
(defvar +modeline--alist nil)
(defun +modeline--segment-active-p (segment xs)
(cond ((null xs) nil)
((listp xs)
(or (+modeline--segment-active-p segment (car xs))
(+modeline--segment-active-p segment (cdr xs))))
((eq xs segment))))
;;;###autoload
(defun +modeline-segment-active-p (segment)
(or (+modeline--segment-active-p segment +modeline-format-left)
(+modeline--segment-active-p segment +modeline-format-right)))
;;;###autodef
(defun def-modeline-format! (name left &optional right)
"Define a preset modeline format by name.
NAME is a symbol. The convention is to use keywords for global formats, like
:main or :project, but to use regular symbols for buffer-local formats, like
'twitter and 'pdf.
LEFT and RIGHT are lists that assume the same structure as `mode-line-format',
and make up the mode-line in two parts, separated by variable-width space, to
keep them left and right aligned respectively."
(setf (alist-get name +modeline--alist) (list left right)))
;;;###autodef
(defmacro def-modeline-segment! (name &rest rest)
"TODO"
(declare (doc-string 2))
(let ((docstring (if (and (stringp (car rest)) (cdr rest)) (pop rest)))
body)
(macroexp-progn
(if (not (keywordp (car rest)))
(append `((defvar-local ,name nil ,docstring)
(put ',name 'risky-local-variable t))
(if (or (stringp (car rest))
(memq (car (car-safe rest)) '(:eval :propertize)))
`((setq-default ,name ,(car rest)))
(let ((fn (intern (format "+modeline--%s" name))))
`((fset ',fn (lambda () ,@rest))
(byte-compile ',fn)
(setq-default ,name (quote (:eval (,fn))))))))
;; isolate body
(setq body rest)
(while (keywordp (car body))
(setq body (cddr body)))
;;
(cl-destructuring-bind (&key init faces on-hooks on-set &allow-other-keys)
rest
(let ((realvar (if (and body faces)
(intern (format "+modeline--var-%s" name))
name)))
(append (when body
(if (or on-hooks on-set)
(let ((setterfn (intern (format "+modeline--set-%s" name)))
(varsetterfn (intern (format "+modeline--setvar-%s" name))))
(append `((fset ',setterfn
(lambda (&rest _)
(when (+modeline-segment-active-p ',name)
(setq-local ,realvar ,(macroexp-progn body)))))
(byte-compile ',setterfn))
(mapcar (lambda (hook) `(add-hook ',hook #',setterfn))
on-hooks)
(when on-set
`((fset ',varsetterfn
(lambda (sym val op where)
(and (eq op 'set) where
(with-current-buffer where
(set sym val)
(,setterfn)))))
,@(mapcan (lambda (var) `((add-variable-watcher ',var #',varsetterfn)))
on-set)))))
(setq init `(quote (:eval ,(macroexp-progn body))))
nil))
(if (eq realvar name)
`((defvar-local ,name nil ,docstring)
(setq-default ,name ,init))
`((defvar-local ,realvar ,init)
(defvar-local ,name nil ,docstring)
(setq-default
,name '(:eval (cond ((active) ,realvar)
(,realvar (substring-no-properties ,realvar)))))))
`((put ',name 'risky-local-variable t)))))))))
;;;###autodef
(defun set-modeline! (name &optional default)
"Replace the current buffer's modeline with a preset mode-line format defined
with `def-modeline-format!'.
If DEFAULT is non-nil, make it the default mode-line for all buffers."
(cl-check-type name symbol)
(let ((modeline (cdr (assq name +modeline--alist))))
(unless modeline
(error "The %s modeline format does not exist" name))
(if default
(setq-default +modeline-format-left `("" ,@(car modeline))
+modeline-format-right `("" ,@(cadr modeline)))
(setq +modeline-format-left `("" ,@(car modeline))
+modeline-format-right `("" ,@(cadr modeline))))
(force-mode-line-update)))

View file

@ -1,750 +1,53 @@
;;; ui/modeline/config.el -*- lexical-binding: t; -*-
;; This mode-line is experimental, Emacs 26+ only, may have buggy and is likely
;; to change. It also isn't feature complete, compared to :ui doom-modeline, but
;; it will eventually replace it.
;;
;; However, it is at least ten times faster than the original modeline, and more
;; flexible, what with `+modeline-format-left', `+modeline-format-right', and a
;; more powerful API for defining modelines and modeline segments.
;; TODO Add themes (default, minimal, spacemacs, etc)
;;;; Benchmarks
;; (benchmark-run 1000 (format-mode-line mode-line-format))
;; Old system: ~0.563 - 0.604
;; New system: ~0.036 - 0.061
(def-package! doom-modeline
:hook (doom-post-init . doom-modeline-mode)
:preface
;; prevent flash of unstyled modeline at startup
(setq-default mode-line-format nil)
;; We display project info in the modeline ourselves
(setq projectile-dynamic-mode-line nil)
:init
(setq doom-modeline-bar-width 3
doom-modeline-github nil
doom-modeline-mu4e nil
doom-modeline-persp-name nil
doom-modeline-checker-simple-format nil
doom-modeline-minor-modes nil
doom-modeline-major-mode-icon nil
doom-modeline-buffer-file-name-style 'relative-from-project)
(defvar +modeline-width 3
"How wide the mode-line bar should be (only respected in GUI emacs).")
(add-hook 'doom-modeline-mode-hook #'size-indication-mode) ; filesize in modeline
(add-hook 'doom-modeline-mode-hook #'column-number-mode) ; cursor column in modeline
(defvar +modeline-height 25
"How tall the mode-line should be (only respected in GUI emacs).")
:config
(add-hook 'doom-big-font-mode-hook #'+modeline|resize-for-big-font)
(defvar +modeline-bar-at-end nil
"If non-nil, the bar is placed at the end, instead of at the beginning of the
modeline.")
(add-hook 'doom-load-theme-hook #'doom-modeline-refresh-bars)
(add-hook '+doom-dashboard-mode-hook #'doom-modeline-set-project-modeline)
(defvar +modeline-bar-invisible nil
"If non-nil, the bar is transparent, and only used to police the height of the
mode-line.")
;; Remove unused segments & extra padding
(doom-modeline-def-modeline 'main
'(bar matches buffer-info remote-host buffer-position selection-info)
'(misc-info persp-name irc mu4e github debug input-method buffer-encoding lsp major-mode process vcs checker))
(defvar +modeline-buffer-path-function #'+modeline-file-path-with-project
"A function that returns, in list form, components of the buffer file name
display in the mode-line.
(doom-modeline-def-modeline 'special
'(bar matches buffer-info-simple buffer-position selection-info)
'(misc-info persp-name debug input-method irc-buffers buffer-encoding lsp major-mode process checker))
Each item should either be a string or a a cons cell whose CAR is the path
component and CDR is the name of a face.
Currently available functions:
+ `+modeline-file-path-with-project': project/src/lib/file.c
+ `+modeline-file-path-from-project': src/lib/file.c
+ `+modeline-file-path-truncated-with-project': project/s/l/file.c
+ `+modeline-file-path-truncated-upto-project': ~/w/project/src/lib/file.c
+ `+modeline-file-path-truncated-upto-project-root': ~/w/p/s/lib/file.c
+ `+modeline-file-path-truncated': ~/w/p/s/l/file.c
+ `+modeline-file-name': file.c")
;; Convenience aliases
(defvaralias 'mode-line-format-left '+modeline-format-left)
(defvaralias 'mode-line-format-right '+modeline-format-right)
;;
(defvar-local +modeline-format-left () "TODO")
(defvar-local +modeline-format-right () "TODO")
(put '+modeline-format-left 'risky-local-variable t)
(put '+modeline-format-right 'risky-local-variable t)
;; Otherwise appended segments will produce *Invalid*
(setq global-mode-string '(""))
;; We handle this ourselves
(setq projectile-dynamic-mode-line nil)
;;
(defvar +modeline--vspc (propertize " " 'face 'variable-pitch))
;; externs
(defvar anzu--state nil)
(defvar evil-mode nil)
(defvar evil-state nil)
(defvar evil-visual-selection nil)
(defvar evil-visual-beginning nil)
(defvar evil-visual-end nil)
(defvar iedit-mode nil)
(defvar all-the-icons-scale-factor)
(defvar all-the-icons-default-adjust)
(doom-modeline-def-modeline 'project
'(bar buffer-default-directory)
'(misc-info mu4e github debug fancy-battery " " major-mode)))
;;
;; Custom faces
(defgroup +modeline nil
"TODO"
:group 'faces)
(defface doom-modeline-buffer-path
'((t (:inherit (mode-line-emphasis bold))))
"Face used for the dirname part of the buffer path."
:group '+modeline)
(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 '+modeline)
(defface doom-modeline-buffer-project-root
'((t (:inherit doom-modeline-buffer-path)))
"Face used for the project root at the beginning of the mode-line path."
:group '+modeline)
(defface doom-modeline-buffer-modified '((t (:inherit (error bold) :background nil)))
"Face used for the 'unsaved' symbol in the mode-line."
:group '+modeline)
(defface doom-modeline-buffer-major-mode '((t (:inherit (mode-line-emphasis bold))))
"Face used for the major-mode segment in the mode-line."
:group '+modeline)
(defface doom-modeline-highlight '((t (:inherit mode-line-emphasis)))
"Face for bright segments of the mode-line."
:group '+modeline)
(defface doom-modeline-panel '((t (:inherit mode-line-highlight)))
"Face for 'X out of Y' segments, such as `+modeline--anzu',
`+modeline--evil-substitute' and `iedit'"
:group '+modeline)
(defface doom-modeline-info `((t (:inherit (success bold))))
"Face for info-level messages in the modeline. Used by `*vc'."
:group '+modeline)
(defface doom-modeline-warning `((t (:inherit (warning bold))))
"Face for warnings in the modeline. Used by `*flycheck'"
:group '+modeline)
(defface doom-modeline-urgent `((t (:inherit (error bold))))
"Face for errors in the modeline. Used by `*flycheck'"
:group '+modeline)
(defface doom-modeline-bar '((t (:inherit highlight)))
"The face used for the left-most bar on the mode-line of an active window."
:group '+modeline)
;;
;; Packages
;; Extensions
(def-package! anzu
:after-call isearch-mode
:config
(setq anzu-cons-mode-line-p nil
anzu-minimum-input-length 1
anzu-search-threshold 250)
(global-anzu-mode +1)
(defun +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 #'+modeline*fix-anzu-count)
;; 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 'doom-escape-hook #'anzu--reset-status t)
(add-hook 'iedit-mode-end-hook #'anzu--reset-status))
:after-call isearch-mode)
(def-package! evil-anzu
:when (featurep! :feature evil)
:after-call (evil-ex-start-search evil-ex-start-word-search))
;;
;; Hacks
;; Keep `+modeline-current-window' up-to-date
(defvar +modeline-current-window (frame-selected-window))
(defun +modeline|set-selected-window (&rest _)
"Sets `+modeline-current-window' appropriately"
(when-let* ((win (frame-selected-window)))
(unless (minibuffer-window-active-p win)
(setq +modeline-current-window win)
(force-mode-line-update))))
(defun +modeline|unset-selected-window ()
(setq +modeline-current-window nil)
(force-mode-line-update))
(add-hook 'window-configuration-change-hook #'+modeline|set-selected-window)
(add-hook 'doom-enter-window-hook #'+modeline|set-selected-window)
(if (not (boundp 'after-focus-change-function))
(progn
(add-hook 'focus-in-hook #'+modeline|set-selected-window)
(add-hook 'focus-out-hook #'+modeline|unset-selected-window))
(defun +modeline|refresh-frame ()
(setq +modeline-current-window nil)
(cl-loop for frame in (frame-list)
if (eq (frame-focus-state frame) t)
return (setq +modeline-current-window (frame-selected-window frame)))
(force-mode-line-update t))
(add-function :after after-focus-change-function #'+modeline|refresh-frame))
(defsubst active ()
(eq (selected-window) +modeline-current-window))
;; Ensure modeline is inactive when Emacs is unfocused (and active otherwise)
(defvar +modeline-remap-face-cookies nil)
(defun +modeline|focus-all-windows (&rest _)
(cl-loop for (buffer . cookie) in +modeline-remap-face-cookies
if (buffer-live-p buffer)
do (with-current-buffer buffer
(face-remap-remove-relative cookie))))
(defun +modeline|unfocus-all-windows (&rest _)
(setq +modeline-remap-face-cookies
(cl-loop for window in (window-list)
for buffer = (window-buffer window)
if (buffer-live-p buffer)
collect
(with-current-buffer buffer
(cons buffer
(face-remap-add-relative 'mode-line
'mode-line-inactive))))))
(add-hook 'focus-in-hook #'+modeline|focus-all-windows)
(add-hook 'focus-out-hook #'+modeline|unfocus-all-windows)
(advice-add #'posframe-hide :after #'+modeline|focus-all-windows)
(advice-add #'posframe-delete :after #'+modeline|focus-all-windows)
(when (featurep! :completion helm)
(add-hook 'helm-before-initialize-hook #'+modeline|unfocus-all-windows)
(add-hook 'helm-cleanup-hook #'+modeline|focus-all-windows))
;;
;; Helpers
(defun +modeline--make-xpm (width height &optional color)
"Create an XPM bitmap. Inspired by `powerline''s `pl/make-xpm'."
(propertize
" " 'display
(let ((data (make-list height (make-list width 1)))
(color (or color "None")))
(ignore-errors
(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)
(cl-loop with idx = 0
with len = (length data)
for dl in data
do (cl-incf idx)
concat "\""
concat (cl-loop for d in dl
if (= d 0) collect (string-to-char " ")
else collect (string-to-char "."))
concat (if (eq idx len) "\"};" "\",\n")))
'xpm t :ascent 'center)))))
(defun +modeline-build-path (path)
"Construct the file path for the `+modeline-buffer-id' segment using
`+mdoeline-buffer-path-function'. If the buffer has no `buffer-file-name', just
use `buffer-name'."
(let ((buffer-file-name (or path buffer-file-name)))
(if (or (eq major-mode 'dired-mode)
(null buffer-file-name))
(propertize "%s" 'face 'doom-modeline-buffer-path)
(cl-loop for spec in (funcall +modeline-buffer-path-function)
if (stringp spec) concat spec
else if (not (null spec))
concat (propertize (car spec) 'face (cdr spec))))))
;;
;; Buffer file path styles
(defun +modeline-file-path-with-project ()
"Returns the unaltered buffer file path relative to the project root's
parent.
e.g. project/src/lib/file.c"
(let* ((base (buffer-base-buffer))
(filename (file-truename (buffer-file-name base))))
(append (if (doom-project-p)
(let* ((project-root (doom-project-root))
(relative-dirs (file-relative-name (file-name-directory filename)
(file-truename project-root))))
(list (cons (concat (doom-project-name) "/")
'doom-modeline-buffer-project-root)
(unless (equal "./" relative-dirs)
(cons relative-dirs 'doom-modeline-buffer-path))))
(list nil (cons (abbreviate-file-name (file-name-directory filename))
'doom-modeline-buffer-path)))
(list (cons (file-name-nondirectory filename)
'doom-modeline-buffer-file)))))
(defun +modeline-file-path-from-project ()
"Returns file path relative to the project root.
e.g. src/lib/file.c
Meant for `+modeline-buffer-path-function'."
(cdr (+modeline-file-path-with-project)))
(defun +modeline-file-path-truncated-with-project ()
"Returns file path relative to (and including) project root, with descendent
folders truncated.
e.g. project/s/l/file.c
Meant for `+modeline-buffer-path-function'."
(let* ((parts (+modeline-file-path-with-project))
(dirs (car (nth 1 parts))))
(setcar (nth 1 parts)
(shrink-path--dirs-internal dirs t))
parts))
(defun +modeline-file-path-truncated-upto-project ()
"Returns file path, truncating segments prior to the project.
e.g. ~/w/project/src/lib/file.c
Meant for `+modeline-buffer-path-function'."
(pcase-let
((`(,root-parent ,root ,dir, file)
(let ((buffer-file-name (or buffer-file-name (buffer-file-name (buffer-base-buffer)))))
(shrink-path-file-mixed (or (doom-project-root) default-directory)
(file-name-directory buffer-file-name)
buffer-file-name))))
(list (cons root-parent 'font-lock-comment-face)
(cons root 'doom-modeline-buffer-project-root)
(cons (concat "/" dir) 'doom-modeline-buffer-path)
(cons file 'doom-modeline-buffer-file))))
(defun +modeline-file-path-truncated-upto-project-root ()
"Return file path, truncating segemnts prior to (and including) the project
root.
e.g. ~/w/p/src/lib/file.c
Meant for `+modeline-buffer-path-function'."
(let* ((parts (+modeline-file-path-truncated-upto-project))
(root (car (nth 1 parts))))
(setcar (nth 1 parts)
(let ((first (substring root 0 1)))
(if (equal first ".")
(substring root 0 2)
first)))
parts))
(defun +modeline-file-path-truncated ()
"Return absolute file path with all directories truncated.
e.g. ~/w/p/s/l/file.c
Meant for `+modeline-buffer-path-function'."
(pcase-let ((`(,dir . ,file)
(shrink-path-prompt (buffer-file-name (buffer-base-buffer)))))
(list (cons dir 'doom-modeline-buffer-path)
(cons file 'doom-modeline-buffer-file))))
(defun +modeline-file-name ()
"Return buffer name.
e.g. file.c
Meant for `+modeline-buffer-path-function'."
(list (cons "%b" 'doom-modeline-buffer-path)))
;;
;; Bars
(defvar +modeline-bar-start nil "TODO")
(put '+modeline-bar-start 'risky-local-variable t)
(defvar +modeline-bar-end nil "TODO")
(put '+modeline-bar-end 'risky-local-variable t)
(defvar +modeline-bar-active nil "TODO")
(defvar +modeline-bar-inactive nil "TODO")
(defun +modeline|setup-bars ()
(setq +modeline-bar-active
(+modeline--make-xpm +modeline-width +modeline-height
(unless +modeline-bar-invisible
(face-background 'doom-modeline-bar nil t)))
+modeline-bar-inactive
(+modeline--make-xpm +modeline-width +modeline-height))
(setq +modeline-bar-start nil
+modeline-bar-end nil)
(if +modeline-bar-at-end
(setq +modeline-bar-end '+modeline-bar)
(setq +modeline-bar-start '+modeline-bar)))
(add-hook 'doom-load-theme-hook #'+modeline|setup-bars)
(defun +modeline|setup-bars-after-change (sym val op _where)
(when (eq op 'set)
(set sym val)
(+modeline|setup-bars)))
(add-variable-watcher '+modeline-width #'+modeline|setup-bars-after-change)
(add-variable-watcher '+modeline-height #'+modeline|setup-bars-after-change)
(add-variable-watcher '+modeline-bar-at-end #'+modeline|setup-bars-after-change)
(add-variable-watcher '+modeline-bar-invisible #'+modeline|setup-bars-after-change)
(def-modeline-segment! +modeline-bar
(if (active) +modeline-bar-active +modeline-bar-inactive))
;;
;; Segments
(def-modeline-segment! +modeline-buffer-state
(let* ((base (buffer-base-buffer))
(icon (cond (buffer-read-only
(all-the-icons-octicon
"lock"
:face 'doom-modeline-warning
:v-adjust -0.05))
((buffer-modified-p base)
(all-the-icons-faicon
"floppy-o"
:face 'doom-modeline-buffer-modified
:v-adjust -0.05))
((and (buffer-file-name base)
(not (file-exists-p (buffer-file-name base))))
(all-the-icons-octicon
"circle-slash"
:face 'doom-modeline-urgent
:v-adjust -0.05)))))
(if icon (concat icon " "))))
(def-modeline-segment! +modeline-buffer-id
:on-hooks (find-file-hook after-save-hook after-revert-hook)
:init (propertize "%b" 'face 'doom-modeline-buffer-file)
:faces t
(let ((file-path (buffer-file-name (buffer-base-buffer))))
(propertize (+modeline-build-path file-path)
'help-echo file-path)))
(def-modeline-segment! +modeline-buffer-directory
(let ((face (if (active) 'doom-modeline-buffer-path)))
(propertize
(concat (if (display-graphic-p) " ")
(all-the-icons-octicon
"file-directory"
:face face
:v-adjust -0.1
:height 1.25)
" "
(propertize (abbreviate-file-name default-directory)
'face face))
'help-echo default-directory)))
(def-modeline-segment! +modeline-vcs
:on-set (vc-mode)
(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 (active))
(all-the-icons-default-adjust -0.1))
(concat (cond ((memq state '(edited added))
(if active (setq face 'doom-modeline-info))
(all-the-icons-octicon
"git-compare"
:face face
:v-adjust -0.05))
((eq state 'needs-merge)
(if active (setq face 'doom-modeline-info))
(all-the-icons-octicon "git-merge" :face face))
((eq state 'needs-update)
(if active (setq face 'doom-modeline-warning))
(all-the-icons-octicon "arrow-down" :face face))
((memq state '(removed conflict unregistered))
(if active (setq face 'doom-modeline-urgent))
(all-the-icons-octicon "alert" :face face))
(t
(if active (setq face 'font-lock-doc-face))
(all-the-icons-octicon
"git-compare"
:face face
:v-adjust -0.05)))
+modeline--vspc
(propertize (substring vc-mode (+ (if (eq backend 'Hg) 2 3) 2))
'face (if active face)))))))
(def-modeline-segment! +modeline-indent-style
:on-hooks (after-revert-hook after-save-hook find-file-hook)
:on-set (indent-tabs-mode tab-width)
(propertize (format "%s%d "
(if indent-tabs-mode "" "")
tab-width)
'help-echo
(format "Indentation: %d %s wide"
tab-width
(if indent-tabs-mode "tabs" "spaces"))))
(def-modeline-segment! +modeline-encoding
:on-hooks (after-revert-hook after-save-hook find-file-hook)
:on-set (buffer-file-coding-system)
(concat (pcase (coding-system-eol-type buffer-file-coding-system)
(0 (propertize "LF" 'help-echo "EOL convention: \\n (Unix)"))
(1 (propertize "CRLF" 'help-echo "EOL convention: \\r\\n (Windows, Symbian OS, etc)"))
(2 (propertize "CR" 'help-echo "EOL convention: \\r (pre-OSX MacOS)")))
" "
(let* ((sys (coding-system-plist buffer-file-coding-system))
(category (plist-get sys :category)))
(propertize
(cond ((eq category 'coding-category-undecided)
"")
((or (eq category 'coding-category-utf-8)
(string-match-p "utf-8" (symbol-name (plist-get sys :name))))
"")
((concat (upcase (symbol-name (plist-get sys :name)))
" ")))
'help-echo (plist-get (coding-system-plist buffer-file-coding-system) :docstring)))))
(def-modeline-segment! +modeline-major-mode
(propertize (format-mode-line mode-name)
'face (if (active) 'doom-modeline-buffer-major-mode)))
(defun +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 +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))
((format " %s/%d " here total))))
'face (if (active) 'doom-modeline-panel))))
(defsubst +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 +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))))
(defun +doom-ml-octicon (icon &optional text face voffset padding)
"Displays an octicon ICON with FACE, followed by TEXT. Uses
`all-the-icons-octicon' to fetch the icon."
(let ((padding-str (if padding (propertize (make-string padding ? ) 'face face))))
(concat padding-str
(when icon
(all-the-icons-octicon icon :face face :height 1.1 :v-adjust (or voffset -0.2)))
(if text (propertize (concat " " text) 'face face))
padding-str)))
(defsubst +modeline--multiple-cursors ()
"Show the number of multiple cursors."
(cond ((bound-and-true-p multiple-cursors-mode)
(propertize
(concat " mc " (eval (cadadr mc/mode-line)) " ")
'face (if (active) 'doom-modeline-panel)))
((bound-and-true-p evil-mc-cursor-list)
(+doom-ml-octicon (if evil-mc-frozen "pin" "pencil")
(number-to-string (length evil-mc-cursor-list))
(if (active) 'doom-modeline-panel)
0
1))))
(def-modeline-segment! +modeline-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 (+modeline--macro-recording)
(+modeline--anzu)
(+modeline--evil-substitute)
(+modeline--iedit)
(+modeline--multiple-cursors)
" ")))
(or (and (not (equal meta " ")) meta)
(if buffer-file-name " %I "))))
;;
(defsubst doom-column (pos)
(save-excursion (goto-char pos)
(current-column)))
(defvar-local +modeline-enable-word-count nil
"If non-nil, a word count will be added to the selection-info modeline
segment.")
(defun +modeline|enable-word-count ()
(setq +modeline-enable-word-count t))
(add-hook 'text-mode-hook #'+modeline|enable-word-count)
(def-modeline-segment! +modeline-selection-info
(let ((beg (or evil-visual-beginning (region-beginning)))
(end (or evil-visual-end (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))
(let ((cols (abs (- (doom-column end)
(doom-column beg)))))
(format "%dx%dB" lines cols)))
((eq evil-visual-selection 'line)
(format "%dL" lines))
((> lines 1)
(format "%dC %dL" (- end beg) lines))
((format "%dC" (- end beg))))
(when +modeline-enable-word-count
(format " %dW" (count-words beg end)))))
'face 'doom-modeline-highlight)))
(defun +modeline|enable-selection-info ()
(add-to-list '+modeline-format-left '+modeline-selection-info t #'eq))
(defun +modeline|disable-selection-info ()
(setq +modeline-format-left (delq '+modeline-selection-info +modeline-format-left)))
(cond ((featurep 'evil)
(add-hook 'evil-visual-state-entry-hook #'+modeline|enable-selection-info)
(add-hook 'evil-visual-state-exit-hook #'+modeline|disable-selection-info))
((add-hook 'activate-mark-hook #'+modeline|enable-selection-info)
(add-hook 'deactivate-mark-hook #'+modeline|disable-selection-info)))
;; flycheck
(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 (when icon
(concat
(all-the-icons-material icon :face face :height 1.1 :v-adjust (or voffset -0.2))
(if text +modeline--vspc)))
(if text (propertize text 'face face))))
(defun +modeline-flycheck-status (status)
(pcase status
(`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))))
(defun +doom-modeline|update-flycheck-segment (&optional status)
(setq +modeline-flycheck
(when-let* ((status-str (+modeline-flycheck-status status)))
(concat +modeline--vspc status-str " "))))
(add-hook 'flycheck-mode-hook #'+doom-modeline|update-flycheck-segment)
(add-hook 'flycheck-status-changed-functions #'+doom-modeline|update-flycheck-segment)
(def-modeline-segment! +modeline-flycheck
"Displays color-coded flycheck error status in the current buffer with pretty
icons."
:init nil)
;;
;; Preset modeline formats
(def-modeline-format! :main
'(+modeline-matches " "
+modeline-buffer-state
+modeline-buffer-id
" %2l:%c %p ")
`(mode-line-misc-info
+modeline-indent-style
+modeline-encoding
+modeline-major-mode " "
(vc-mode (" " +modeline-vcs " "))
mode-line-process
+modeline-flycheck))
(def-modeline-format! :minimal
'(+modeline-matches " "
+modeline-buffer-state
+modeline-buffer-id)
'(+modeline-major-mode))
(def-modeline-format! :special
'(+modeline-matches +modeline-buffer-state " %b " +modeline-buffer-position)
'(+modeline-encoding +modeline-major-mode mode-line-process))
(def-modeline-format! :project
'(+modeline-buffer-directory)
'(+modeline-major-mode))
;;
(def-modeline-segment! +modeline--rest
(let ((rhs-str (format-mode-line +modeline-format-right)))
(list (propertize
" " 'display
`((space :align-to (- (+ right right-fringe right-margin)
,(1+ (string-width rhs-str))))))
rhs-str)))
(setq-default mode-line-format '("" +modeline-bar-start +modeline-format-left +modeline--rest +modeline-bar-end))
;; Set the default modeline as late as possible, giving users a chance to change
;; the above formats.
(add-hook! 'after-init-hook (set-modeline! :main t))
;; Set special modelines for special buffers
(add-hook! '+doom-dashboard-mode-hook (set-modeline! :project))
(add-hook! 'doom-scratch-buffer-hook (set-modeline! :special))

View file

@ -1,20 +1,7 @@
;; -*- no-byte-compile: t; -*-
;;; ui/modeline/packages.el
;;; These are the invisible dependencies
;; Required
;;(require 'evil)
;;(require 'projectile)
;;(require 'all-the-icons)
;; Optional
;;(require 'flycheck)
;;(require 'iedit)
;;(require 'evil-multiedit)
(package! doom-modeline)
(package! anzu)
(when (featurep! :feature evil)
(package! evil-anzu))
(package! shrink-path)