From 2cc6dfe73c3ffc4444138c84493b07fc15b63a80 Mon Sep 17 00:00:00 2001 From: Levi Olson Date: Mon, 4 Mar 2019 16:09:53 -0600 Subject: [PATCH] Doom modeline; chrome extension to edit text in browser; openHAB support; mail with mu4e; --- init.el | 973 ++++++++++++--------------------------------------- init.elc | Bin 51184 -> 27589 bytes init.org | 1031 ++++++++++++++---------------------------------------- 3 files changed, 479 insertions(+), 1525 deletions(-) diff --git a/init.el b/init.el index 195d04c..6097bc7 100644 --- a/init.el +++ b/init.el @@ -20,6 +20,7 @@ '(all-the-icons anzu base16-theme + bbdb better-defaults company company-go @@ -28,6 +29,7 @@ dash-at-point diminish dockerfile-mode + doom-modeline doom-themes ein eldoc-eval @@ -46,6 +48,7 @@ magit material-theme multiple-cursors + nnir-est projectile py-autopep8 rainbow-delimiters @@ -67,6 +70,10 @@ (when (not (package-installed-p p)) (package-install p))) +(require 'edit-server) + +(edit-server-start) + (require 'better-defaults) ;; Instead of the annoying giant warning icon, just flash the modeline. @@ -116,6 +123,10 @@ (add-to-list 'default-frame-alist (cons 'top 0)) (add-to-list 'default-frame-alist (cons 'left 1000)) +(put 'narrow-to-region 'disabled nil) +(put 'upcase-region 'disabled nil) +(put 'downcase-region 'disabled nil) + (setq inhibit-splash-screen nil fancy-splash-image "~/.emacs.d/public/emacs-logo.png" fancy-splash-image-file "~/.emacs.d/public/emacs-logo.png") @@ -145,6 +156,89 @@ (defalias 'yes-or-no-p 'y-or-n-p) +(require 'font-lock) + +(defvar openhab-mode-hook nil) + +(defvar openhab-mode-map + (let ((map (make-keymap))) + (define-key map "\C-j" 'newline-and-indent) + map) + "Keymap for OPENHAB major mode.") + +(add-to-list 'auto-mode-alist '("\\.sitemap\\'" . openhab-mode)) +(add-to-list 'auto-mode-alist '("\\.items\\'" . openhab-mode)) +(add-to-list 'auto-mode-alist '("\\.rules\\'" . openhab-mode)) +(add-to-list 'auto-mode-alist '("\\.things\\'" . openhab-mode)) + +(defconst openhab-font-lock-keywords + `( + ("\<.*\>" . font-lock-constant-face) + (,(regexp-opt + '( + ;; KEYWORDS + "Selection" "Slider" "List" "Setpoint" "Video" "Chart" "Webview" "Colorpicker" + "Timer" "Number" "String" + "Switch" "Rollershutter" "Number" "String" "Dimmer" "Contact" "DateTime" "Color" + "Text" "Group" "Image" "Frame" + "Thing" "Bridge" + "Time" "System" + "sitemap" + + "rule" "when" "then" "end" + "if" "val" + "import" "var" "say" "postUpdate" "switch" "println" "case" "or" "sendCommand" + ) + 'words) + (1 font-lock-keyword-face)) + (,(regexp-opt + '( + "ON" "OFF" "on" "off" + "AND" "OR" "NAND" "NOR" "AVG" "SUM" "MAX" "MIN" + "true" "false" + ) + 'words) + (1 font-lock-constant-face)) + (,(regexp-opt + '( + "name" "label" "item" "period" "refresh" "icon" "mappings" "minValue" "maxValue" "step" "switchsupport" "url" "height" "refresh" "visibility" "valuecolor" + ) + 'words) + (1 font-lock-type-face)) + ("\(.*\)" . font-lock-variable-name-face) + ("[^a-zA-Z0-9_:]\\([0-9]*\\)[^a-zA-Z0-9_:]" . (1 font-lock-variable-name-face)) + ("\s@\s" . font-lock-variable-name-face) + ("\s\\([a-zA-Z0-9_:]*\\)\\(\s\\|$\\)" . (1 font-lock-type-face)) + ("=\\([a-zA-Z_]*\\)" . (1 font-lock-string-face)) + ("\\([a-zA-Z]*\\)=" . (1 font-lock-type-face)) + ) + "The regexps to highlight in openHAB mode.") + +(defvar openhab-mode-syntax-table + (let ((st (make-syntax-table))) + (modify-syntax-entry ?/ ". 12b" st) ;; C-style comments // ... + (modify-syntax-entry ?\n "> b" st) ;; \n ends comment + ;; Block comments /*...*/ + (modify-syntax-entry ?\/ ". 14" st) + (modify-syntax-entry ?* ". 23" st) + st) + "Syntax table for openhab-mode.") + +(defun openhab-mode () + "Major mode for editing OPENHAB config files." + (interactive) + (kill-all-local-variables) + (set-syntax-table openhab-mode-syntax-table) + (use-local-map openhab-mode-map) + (set (make-local-variable 'font-lock-defaults) '(openhab-font-lock-keywords nil t)) + (electric-pair-mode -1) + (flycheck-mode -1) + (setq major-mode 'openhab-mode) + (setq mode-name "OpenHAB") + (run-hooks 'openhab-mode-hook)) + +(provide 'openhab-mode) + (require 'which-key) (which-key-setup-minibuffer) (which-key-mode) @@ -214,6 +308,87 @@ (global-set-key (kbd "C-c g") 'magit-status) (setq magit-completing-read-function 'ivy-completing-read) +(add-to-list 'load-path "/usr/local/share/emacs/site-lisp/mu/mu4e") +(require 'mu4e) + +;; default +(setq mu4e-maildir "~/Mail" + mu4e-mu-binary "/usr/local/bin/mu" + mu4e-get-mail-command "offlineimap" ;; Allow updating with the "U" command + mu4e-sent-messages-behavior 'delete ;; Delete sent messages + mu4e-view-show-images t ;; attempt to show images + mu4e-view-image-max-width 400 ;; max image size + message-kill-buffer-on-exit t ;; don't keep messages around + mu4e-use-fancy-chars t ;; use 'fancy' chars + mu4e-update-interval 300 ;; 5 mins + ) + +(setq mu4e-contexts + `( ,(make-mu4e-context + :name "Vlocity" + :enter-func (lambda () (mu4e-message "Entering Vlocity")) + :leave-func (lambda () (mu4e-message "Leaving Vlocity")) + ;; we match based on the contact-fields of the message + :match-func (lambda (msg) + (when msg + (string= (mu4e-message-field msg :maildir) "/Vlocity"))) + :vars '( ( user-mail-address . "lolson@vlocity.com" ) + ( smtpmail-mail-address . "lolson@vlocity.com" ) + ( user-full-name . "Levi Olson" ) + ( mu4e-compose-signature . + (concat + "--\n" + "Levi Olson\n" + "Senior UI Developer")) + ( mu4e-sent-folder . "/Vlocity/[Gmail].Sent Mail" ) + ( mu4e-drafts-folder . "/Vlocity/[Gmail].Drafts" ) + ( mu4e-trash-folder . "/Vlocity/[Gmail].Trash" ) + ( mu4e-maildir-shortcuts . (("/Vlocity/INBOX" . ?i) + ("/Vlocity/[Gmail].Sent Mail" . ?s) + ("/Vlocity/[Gmail].Trash" . ?t) + ("/Vlocity/[Gmail].All Mail" . ?a))))) + ,(make-mu4e-context + :name "Gmail" + :enter-func (lambda () (mu4e-message "Entering Gmail")) + :leave-func (lambda () (mu4e-message "Leaving Gmail")) + ;; this matches maildir /Arkham and its sub-directories + :match-func (lambda (msg) + (when msg + (string= (mu4e-message-field msg :maildir) "/Gmail"))) + :vars '( ( user-mail-address . "olson.levi@gmail.com" ) + ( smtpmail-mail-address . "olson.levi@gmail.com" ) + ( user-full-name . "Levi Olson" ) + ( mu4e-compose-signature . + (concat + "--\n" + "Levi\n")) + ( mu4e-sent-folder . "/Gmail/[Gmail].Sent Mail" ) + ( mu4e-drafts-folder . "/Gmail/[Gmail].Drafts" ) + ( mu4e-trash-folder . "/Gmail/[Gmail].Trash" ) + ( mu4e-maildir-shortcuts . (("/Gmail/INBOX" . ?i) + ("/Gmail/[Gmail].Sent Mail" . ?s) + ("/Gmail/[Gmail].Trash" . ?t) + ("/Gmail/[Gmail].All Mail" . ?a)) + ))))) + +;; (defcustom smtpmail-smtp-user nil +;; "User name to use when looking up credentials in the authinfo file. +;; If non-nil, only consider credentials for the specified user." +;; :version "24.1" +;; :type '(choice (const nil) string) +;; :group 'smtpmail) + + + +;; How to handle HTML emails +;; (setq mu4e-html2text-command "textutil -stdin -format html -convert txt -stdout") + +;; Add option to view HTML in browser +(add-to-list 'mu4e-headers-actions + '("in browser" . mu4e-action-view-in-browser) t) +(add-to-list 'mu4e-view-actions + '("in browser" . mu4e-action-view-in-browser) t) + (require 'projectile) (require 'counsel-projectile) @@ -581,7 +756,7 @@ (global-set-key (kbd "C-x .") 'dash-at-point) (global-set-key (kbd "C-x ,") 'dash-at-point-with-docset) (global-set-key (kbd "C-s") (lambda () (interactive) (swiper (format "%s" (thing-at-point 'symbol))))) - +(global-set-key (kbd "M-m") 'mu4e) ;; (dolist (n (number-sequence 1 9)) ;; (global-set-key (kbd (concat "M-" (int-to-string n))) ;; (lambda () (interactive) (switch-shell n)))) @@ -632,770 +807,70 @@ (cond ((member "PragmataPro" (font-family-list)) (set-face-attribute 'default nil :font "PragmataPro-14"))) -(require 'use-package) -(require 'anzu) -(require 'eldoc-eval) -(require 'iedit) -(require 'projectile) -(require 'all-the-icons) - -(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)))))) - -(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-hook-forms (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 (if (eq hook-fn 'remove-hook) - `(remove-hook ',hook ,fn ,local-p) - `(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)))))) - -(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))) - -(use-package eldoc-eval - :config - (defun +doom-modeline-eldoc (text) - (concat (when (display-graphic-p) - (+doom-modeline--make-xpm - (face-background 'doom-modeline-eldoc-bar nil t) - +doom-modeline-height - +doom-modeline-bar-width)) - text)) - - ;; Show eldoc in the mode-line with `eval-expression' - (defun +doom-modeline--show-eldoc (input) - "Display string STR in the mode-line next to minibuffer." - (with-current-buffer (eldoc-current-buffer) - (let* ((str (and (stringp input) input)) - (mode-line-format (or (and str (or (+doom-modeline-eldoc str) str)) - mode-line-format)) - mode-line-in-non-selected-windows) - (force-mode-line-update) - (sit-for eldoc-show-in-mode-line-delay)))) - (setq eldoc-in-minibuffer-show-fn #'+doom-modeline--show-eldoc) - - (eldoc-in-minibuffer-mode +1)) - -;; anzu and evil-anzu expose current/total state that can be displayed in the -;; mode-line. -(use-package anzu - :init - ;; (add-transient-hook! #'ex-start-search (require 'anzu)) - ;; (add-transient-hook! #'ex-start-word-search (require '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" - (when-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).") +(require 'doom-modeline) +(doom-modeline-mode 1) -(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-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) +;; How tall the mode-line should be (only respected in GUI Emacs). +(setq doom-modeline-height 35) +;; How wide the mode-line bar should be (only respected in GUI Emacs). +(setq doom-modeline-bar-width 4) +;; Determines the style used by `doom-modeline-buffer-file-name'. ;; -;; Custom faces +;; Given ~/Projects/FOSS/emacs/lisp/comint.el +;; truncate-upto-project => ~/P/F/emacs/lisp/comint.el +;; truncate-from-project => ~/Projects/FOSS/emacs/l/comint.el +;; truncate-with-project => emacs/l/comint.el +;; truncate-except-project => ~/P/F/emacs/l/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 +;; buffer-name => comint.el<2> (uniquify buffer name) ;; +;; If you are expereicing the laggy issue, especially while editing remote files +;; with tramp, please try `file-name' style. +;; Please refer to https://github.com/bbatsov/projectile/issues/657. +(setq doom-modeline-buffer-file-name-style 'truncate-upto-project) -(defgroup +doom-modeline nil - "" - :group 'doom) - -(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) +;; What executable of Python will be used (if nil nothing will be showed). +(setq doom-modeline-python-executable "python") +;; Whether show `all-the-icons' or not (if nil nothing will be showed). +(setq doom-modeline-icon t) -;; -;; Modeline helpers -;; +;; Whether show the icon for major mode. It respects `doom-modeline-icon'. +(setq doom-modeline-major-mode-icon t) -(defsubst active () - (eq (selected-window) +doom-modeline-current-window)) - -;; Inspired from `powerline's `pl/make-xpm'. -(defun +doom-modeline--make-xpm (color height width) - "Create an XPM bitmap." - (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)) - ('relative-from-project (+doom-modeline--buffer-file-name-relative '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 (&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 buffer-file-truename))) - (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 buffer-file-name) - 'face (if file-faces `(:inherit ,file-faces))))))))) - -(defun +doom-modeline--buffer-file-name-relative (&optional include-project) - "Propertized `buffer-file-name' showing directories relative to project's root only." - (let ((root (projectile-project-root)) - (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 buffer-file-truename) - (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 buffer-file-truename) - 'face (if file-faces `(:inherit ,file-faces)))))))) - -(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* ((project-root (projectile-project-root)) - (file-name-split (shrink-path-file-mixed project-root - (file-name-directory buffer-file-truename) - buffer-file-truename)) - (active (active))) - (if (null file-name-split) - (propertize "%b" 'face (if active 'doom-modeline-buffer-file)) - (pcase-let ((`(,root-path-parent ,project ,relative-path ,filename) 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 filename 'face file-props))))))))) - - -;; -;; Segments -;; +;; Display color icons for `major-mode'. It respects `all-the-icons-color-icons'. +(setq doom-modeline-major-mode-color-icon nil) -(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)))) +;; Whether display minor modes or not. Non-nil to display in mode-line. +(setq doom-modeline-minor-modes nil) -;; -(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"))) +;; If non-nil, a word count will be added to the selection-info modeline segment. +(setq doom-modeline-enable-word-count nil) -;; -(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)))) +;; If non-nil, only display one number for checker information if applicable. +(setq doom-modeline-checker-simple-format t) -;; -(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)))))) - " ")) +;; Whether display perspective name or not. Non-nil to display in mode-line. +(setq doom-modeline-persp-name t) -;; -(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 (featurep 'face-remap) - (/= text-scale-mode-amount 0) - (format " (%+d)" text-scale-mode-amount))) - 'face (if (active) 'doom-modeline-buffer-major-mode))) +;; Whether display `lsp' state or not. Non-nil to display in mode-line. +(setq doom-modeline-lsp t) -;; -(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))) - (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)) - " "))))) +;; Whether display github notifications or not. Requires `ghub` package. +(setq doom-modeline-github nil) -;; -(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)))) - (or (and (not (equal meta "")) meta) - (if buffer-file-name " %I ")))) - -;; 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) - 'doom-modeline-bar - 'doom-modeline-inactive-bar) - nil t) - +doom-modeline-height - +doom-modeline-bar-width) - "")) - - -;; -;; 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|init () - "Set the default modeline." - (doom-set-modeline 'main t) - - ;; This scratch buffer is already created and doesn't get a modeline. For the - ;; love of Emacs, someone give the man a modeline! - (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)) - -(defun +doom-modeline|set-project-modeline () - (doom-set-modeline 'project)) - - -;; -;; Bootstrap -;; +;; The interval of checking github. +(setq doom-modeline-github-interval (* 30 60)) -(add-hook 'emacs-startup-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) +;; Whether display environment version or not. +(setq doom-modeline-env-version t) -(add-hook 'image-mode-hook #'+doom-modeline|set-media-modeline) -(add-hook 'org-src-mode-hook #'+doom-modeline|set-special-modeline) -(add-hook 'circe-mode-hook #'+doom-modeline|set-special-modeline) +;; Whether display mu4e notifications or not. Requires `mu4e-alert' package. +(setq doom-modeline-mu4e t) diff --git a/init.elc b/init.elc index 0d1863efc5e326329b5430d7faa4c7eb36bd6f1b..656cbdd646b9c3886ff0ea0033a63561624a2174 100644 GIT binary patch delta 7325 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 Pxk8NSRkF&g+TlL|oMEsw literal 51184 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" . font-lock-constant-face) + (,(regexp-opt + '( + ;; KEYWORDS + "Selection" "Slider" "List" "Setpoint" "Video" "Chart" "Webview" "Colorpicker" + "Timer" "Number" "String" + "Switch" "Rollershutter" "Number" "String" "Dimmer" "Contact" "DateTime" "Color" + "Text" "Group" "Image" "Frame" + "Thing" "Bridge" + "Time" "System" + "sitemap" + + "rule" "when" "then" "end" + "if" "val" + "import" "var" "say" "postUpdate" "switch" "println" "case" "or" "sendCommand" + ) + 'words) + (1 font-lock-keyword-face)) + (,(regexp-opt + '( + "ON" "OFF" "on" "off" + "AND" "OR" "NAND" "NOR" "AVG" "SUM" "MAX" "MIN" + "true" "false" + ) + 'words) + (1 font-lock-constant-face)) + (,(regexp-opt + '( + "name" "label" "item" "period" "refresh" "icon" "mappings" "minValue" "maxValue" "step" "switchsupport" "url" "height" "refresh" "visibility" "valuecolor" + ) + 'words) + (1 font-lock-type-face)) + ("\(.*\)" . font-lock-variable-name-face) + ("[^a-zA-Z0-9_:]\\([0-9]*\\)[^a-zA-Z0-9_:]" . (1 font-lock-variable-name-face)) + ("\s@\s" . font-lock-variable-name-face) + ("\s\\([a-zA-Z0-9_:]*\\)\\(\s\\|$\\)" . (1 font-lock-type-face)) + ("=\\([a-zA-Z_]*\\)" . (1 font-lock-string-face)) + ("\\([a-zA-Z]*\\)=" . (1 font-lock-type-face)) + ) + "The regexps to highlight in openHAB mode.") + + (defvar openhab-mode-syntax-table + (let ((st (make-syntax-table))) + (modify-syntax-entry ?/ ". 12b" st) ;; C-style comments // ... + (modify-syntax-entry ?\n "> b" st) ;; \n ends comment + ;; Block comments /*...*/ + (modify-syntax-entry ?\/ ". 14" st) + (modify-syntax-entry ?* ". 23" st) + st) + "Syntax table for openhab-mode.") + + (defun openhab-mode () + "Major mode for editing OPENHAB config files." + (interactive) + (kill-all-local-variables) + (set-syntax-table openhab-mode-syntax-table) + (use-local-map openhab-mode-map) + (set (make-local-variable 'font-lock-defaults) '(openhab-font-lock-keywords nil t)) + (electric-pair-mode -1) + (flycheck-mode -1) + (setq major-mode 'openhab-mode) + (setq mode-name "OpenHAB") + (run-hooks 'openhab-mode-hook)) + + (provide 'openhab-mode) + #+END_SRC ** Tools *** General #+BEGIN_SRC emacs-lisp :results silent @@ -270,6 +367,89 @@ (setq magit-completing-read-function 'ivy-completing-read) #+END_SRC +*** Mu4e +#+BEGIN_SRC emacs-lisp :results silent + (add-to-list 'load-path "/usr/local/share/emacs/site-lisp/mu/mu4e") + (require 'mu4e) + + ;; default + (setq mu4e-maildir "~/Mail" + mu4e-mu-binary "/usr/local/bin/mu" + mu4e-get-mail-command "offlineimap" ;; Allow updating with the "U" command + mu4e-sent-messages-behavior 'delete ;; Delete sent messages + mu4e-view-show-images t ;; attempt to show images + mu4e-view-image-max-width 400 ;; max image size + message-kill-buffer-on-exit t ;; don't keep messages around + mu4e-use-fancy-chars t ;; use 'fancy' chars + mu4e-update-interval 300 ;; 5 mins + ) + + (setq mu4e-contexts + `( ,(make-mu4e-context + :name "Vlocity" + :enter-func (lambda () (mu4e-message "Entering Vlocity")) + :leave-func (lambda () (mu4e-message "Leaving Vlocity")) + ;; we match based on the contact-fields of the message + :match-func (lambda (msg) + (when msg + (string= (mu4e-message-field msg :maildir) "/Vlocity"))) + :vars '( ( user-mail-address . "lolson@vlocity.com" ) + ( smtpmail-mail-address . "lolson@vlocity.com" ) + ( user-full-name . "Levi Olson" ) + ( mu4e-compose-signature . + (concat + "--\n" + "Levi Olson\n" + "Senior UI Developer")) + ( mu4e-sent-folder . "/Vlocity/[Gmail].Sent Mail" ) + ( mu4e-drafts-folder . "/Vlocity/[Gmail].Drafts" ) + ( mu4e-trash-folder . "/Vlocity/[Gmail].Trash" ) + ( mu4e-maildir-shortcuts . (("/Vlocity/INBOX" . ?i) + ("/Vlocity/[Gmail].Sent Mail" . ?s) + ("/Vlocity/[Gmail].Trash" . ?t) + ("/Vlocity/[Gmail].All Mail" . ?a))))) + ,(make-mu4e-context + :name "Gmail" + :enter-func (lambda () (mu4e-message "Entering Gmail")) + :leave-func (lambda () (mu4e-message "Leaving Gmail")) + ;; this matches maildir /Arkham and its sub-directories + :match-func (lambda (msg) + (when msg + (string= (mu4e-message-field msg :maildir) "/Gmail"))) + :vars '( ( user-mail-address . "olson.levi@gmail.com" ) + ( smtpmail-mail-address . "olson.levi@gmail.com" ) + ( user-full-name . "Levi Olson" ) + ( mu4e-compose-signature . + (concat + "--\n" + "Levi\n")) + ( mu4e-sent-folder . "/Gmail/[Gmail].Sent Mail" ) + ( mu4e-drafts-folder . "/Gmail/[Gmail].Drafts" ) + ( mu4e-trash-folder . "/Gmail/[Gmail].Trash" ) + ( mu4e-maildir-shortcuts . (("/Gmail/INBOX" . ?i) + ("/Gmail/[Gmail].Sent Mail" . ?s) + ("/Gmail/[Gmail].Trash" . ?t) + ("/Gmail/[Gmail].All Mail" . ?a)) + ))))) + + ;; (defcustom smtpmail-smtp-user nil + ;; "User name to use when looking up credentials in the authinfo file. + ;; If non-nil, only consider credentials for the specified user." + ;; :version "24.1" + ;; :type '(choice (const nil) string) + ;; :group 'smtpmail) + + + + ;; How to handle HTML emails + ;; (setq mu4e-html2text-command "textutil -stdin -format html -convert txt -stdout") + + ;; Add option to view HTML in browser + (add-to-list 'mu4e-headers-actions + '("in browser" . mu4e-action-view-in-browser) t) + (add-to-list 'mu4e-view-actions + '("in browser" . mu4e-action-view-in-browser) t) +#+END_SRC *** Projectile #+BEGIN_SRC emacs-lisp :results silent (require 'projectile) @@ -663,7 +843,7 @@ (global-set-key (kbd "C-x .") 'dash-at-point) (global-set-key (kbd "C-x ,") 'dash-at-point-with-docset) (global-set-key (kbd "C-s") (lambda () (interactive) (swiper (format "%s" (thing-at-point 'symbol))))) - + (global-set-key (kbd "M-m") 'mu4e) ;; (dolist (n (number-sequence 1 9)) ;; (global-set-key (kbd (concat "M-" (int-to-string n))) ;; (lambda () (interactive) (switch-shell n)))) @@ -718,774 +898,73 @@ (set-face-attribute 'default nil :font "PragmataPro-14"))) #+END_SRC -*** Modeline - #+BEGIN_SRC emacs-lisp :results silent - (require 'use-package) - (require 'anzu) - (require 'eldoc-eval) - (require 'iedit) - (require 'projectile) - (require 'all-the-icons) - - (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)))))) - - (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-hook-forms (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 (if (eq hook-fn 'remove-hook) - `(remove-hook ',hook ,fn ,local-p) - `(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)))))) - - (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))) - - (use-package eldoc-eval - :config - (defun +doom-modeline-eldoc (text) - (concat (when (display-graphic-p) - (+doom-modeline--make-xpm - (face-background 'doom-modeline-eldoc-bar nil t) - +doom-modeline-height - +doom-modeline-bar-width)) - text)) - - ;; Show eldoc in the mode-line with `eval-expression' - (defun +doom-modeline--show-eldoc (input) - "Display string STR in the mode-line next to minibuffer." - (with-current-buffer (eldoc-current-buffer) - (let* ((str (and (stringp input) input)) - (mode-line-format (or (and str (or (+doom-modeline-eldoc str) str)) - mode-line-format)) - mode-line-in-non-selected-windows) - (force-mode-line-update) - (sit-for eldoc-show-in-mode-line-delay)))) - (setq eldoc-in-minibuffer-show-fn #'+doom-modeline--show-eldoc) - - (eldoc-in-minibuffer-mode +1)) - - ;; anzu and evil-anzu expose current/total state that can be displayed in the - ;; mode-line. - (use-package anzu - :init - ;; (add-transient-hook! #'ex-start-search (require 'anzu)) - ;; (add-transient-hook! #'ex-start-word-search (require '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" - (when-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-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) - - - ;; - ;; Custom faces - ;; - - (defgroup +doom-modeline nil - "" - :group 'doom) - - (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) - - - ;; - ;; Modeline helpers - ;; - - (defsubst active () - (eq (selected-window) +doom-modeline-current-window)) - - ;; Inspired from `powerline's `pl/make-xpm'. - (defun +doom-modeline--make-xpm (color height width) - "Create an XPM bitmap." - (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)) - ('relative-from-project (+doom-modeline--buffer-file-name-relative '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 (&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 buffer-file-truename))) - (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 buffer-file-name) - 'face (if file-faces `(:inherit ,file-faces))))))))) - - (defun +doom-modeline--buffer-file-name-relative (&optional include-project) - "Propertized `buffer-file-name' showing directories relative to project's root only." - (let ((root (projectile-project-root)) - (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 buffer-file-truename) - (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 buffer-file-truename) - 'face (if file-faces `(:inherit ,file-faces)))))))) - - (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* ((project-root (projectile-project-root)) - (file-name-split (shrink-path-file-mixed project-root - (file-name-directory buffer-file-truename) - buffer-file-truename)) - (active (active))) - (if (null file-name-split) - (propertize "%b" 'face (if active 'doom-modeline-buffer-file)) - (pcase-let ((`(,root-path-parent ,project ,relative-path ,filename) 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 filename 'face file-props))))))))) - - - ;; - ;; Segments - ;; - - (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)))) - - ;; - (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) - (and (featurep 'face-remap) - (/= text-scale-mode-amount 0) - (format " (%+d)" text-scale-mode-amount))) - 'face (if (active) 'doom-modeline-buffer-major-mode))) - - ;; - (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))) - (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)) - " "))))) - - ;; - (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)))) - (or (and (not (equal meta "")) meta) - (if buffer-file-name " %I ")))) - - ;; 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) - 'doom-modeline-bar - 'doom-modeline-inactive-bar) - nil t) - +doom-modeline-height - +doom-modeline-bar-width) - "")) - - - ;; - ;; 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|init () - "Set the default modeline." - (doom-set-modeline 'main t) - - ;; This scratch buffer is already created and doesn't get a modeline. For the - ;; love of Emacs, someone give the man a modeline! - (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)) - - (defun +doom-modeline|set-project-modeline () - (doom-set-modeline 'project)) - - - ;; - ;; Bootstrap - ;; - - (add-hook 'emacs-startup-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 'org-src-mode-hook #'+doom-modeline|set-special-modeline) - (add-hook 'circe-mode-hook #'+doom-modeline|set-special-modeline) +*** Doom Modeline +#+BEGIN_SRC emacs-lisp :results silent + (require 'doom-modeline) + (doom-modeline-mode 1) - #+END_SRC + + ;; How tall the mode-line should be (only respected in GUI Emacs). + (setq doom-modeline-height 35) + + ;; How wide the mode-line bar should be (only respected in GUI Emacs). + (setq doom-modeline-bar-width 4) + + ;; 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-from-project => ~/Projects/FOSS/emacs/l/comint.el + ;; truncate-with-project => emacs/l/comint.el + ;; truncate-except-project => ~/P/F/emacs/l/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 + ;; buffer-name => comint.el<2> (uniquify buffer name) + ;; + ;; If you are expereicing the laggy issue, especially while editing remote files + ;; with tramp, please try `file-name' style. + ;; Please refer to https://github.com/bbatsov/projectile/issues/657. + (setq doom-modeline-buffer-file-name-style 'truncate-upto-project) + + ;; What executable of Python will be used (if nil nothing will be showed). + (setq doom-modeline-python-executable "python") + + ;; Whether show `all-the-icons' or not (if nil nothing will be showed). + (setq doom-modeline-icon t) + + ;; Whether show the icon for major mode. It respects `doom-modeline-icon'. + (setq doom-modeline-major-mode-icon t) + + ;; Display color icons for `major-mode'. It respects `all-the-icons-color-icons'. + (setq doom-modeline-major-mode-color-icon nil) + + ;; Whether display minor modes or not. Non-nil to display in mode-line. + (setq doom-modeline-minor-modes nil) + + ;; If non-nil, a word count will be added to the selection-info modeline segment. + (setq doom-modeline-enable-word-count nil) + + ;; If non-nil, only display one number for checker information if applicable. + (setq doom-modeline-checker-simple-format t) + + ;; Whether display perspective name or not. Non-nil to display in mode-line. + (setq doom-modeline-persp-name t) + + ;; Whether display `lsp' state or not. Non-nil to display in mode-line. + (setq doom-modeline-lsp t) + + ;; Whether display github notifications or not. Requires `ghub` package. + (setq doom-modeline-github nil) + + ;; The interval of checking github. + (setq doom-modeline-github-interval (* 30 60)) + + ;; Whether display environment version or not. + (setq doom-modeline-env-version t) + + ;; Whether display mu4e notifications or not. Requires `mu4e-alert' package. + (setq doom-modeline-mu4e t) +#+END_SRC