linum-mode *really* slows down buffers when they're displayed in more than one window. This lag isn't present in nlinum. nlinum isn't perfect either but... lesser of two evils. This includes advisors and an ESC hook to mitigate the issue of disappearing nlinum line numbers.
333 lines
13 KiB
EmacsLisp
333 lines
13 KiB
EmacsLisp
;; core-ui.el --- draw me like one of your French editors
|
||
|
||
(defvar doom-ui-fringe-size '4 "Default fringe width")
|
||
|
||
(setq-default
|
||
bidi-display-reordering nil ; disable bidirectional text for tiny performance boost
|
||
blink-matching-paren nil ; don't blink--too distracting
|
||
cursor-in-non-selected-windows nil ; hide cursors in other windows
|
||
frame-inhibit-implied-resize t
|
||
;; remove continuation arrow on right fringe
|
||
fringe-indicator-alist (delq (assq 'continuation fringe-indicator-alist)
|
||
fringe-indicator-alist)
|
||
highlight-nonselected-windows nil
|
||
image-animate-loop t
|
||
indicate-buffer-boundaries nil
|
||
indicate-empty-lines nil
|
||
jit-lock-defer-time nil
|
||
jit-lock-stealth-nice 0.1
|
||
jit-lock-stealth-time 0.2
|
||
jit-lock-stealth-verbose nil
|
||
max-mini-window-height 0.3
|
||
mode-line-default-help-echo nil ; disable mode-line mouseovers
|
||
mouse-yank-at-point t ; middle-click paste at point, not at click
|
||
resize-mini-windows 'grow-only ; Minibuffer resizing
|
||
show-help-function nil ; hide :help-echo text
|
||
split-width-threshold nil ; favor horizontal splits
|
||
uniquify-buffer-name-style 'forward
|
||
use-dialog-box nil ; always avoid GUI
|
||
visible-cursor nil
|
||
x-stretch-cursor nil
|
||
;; `pos-tip' defaults
|
||
pos-tip-internal-border-width 6
|
||
pos-tip-border-width 1
|
||
;; no beeping or blinking please
|
||
ring-bell-function #'ignore
|
||
visible-bell nil
|
||
;; Ask for confirmation on quit only if real buffers exist
|
||
confirm-kill-emacs (lambda (_) (if (doom-real-buffers-list) (y-or-n-p "››› Quit?") t)))
|
||
|
||
(fset #'yes-or-no-p #'y-or-n-p) ; y/n instead of yes/no
|
||
|
||
;; auto-enabled in Emacs 25+; I'd rather enable it manually
|
||
(global-eldoc-mode -1)
|
||
|
||
;; show typed keystrokes in minibuffer
|
||
(setq echo-keystrokes 0.02)
|
||
;; ...but hide them while isearch is active
|
||
(add-hook! isearch-mode (setq echo-keystrokes 0))
|
||
(add-hook! isearch-mode-end (setq echo-keystrokes 0.02))
|
||
|
||
;; A minor mode for toggling the mode-line
|
||
(defvar-local doom--modeline-format nil
|
||
"The modeline format to use when `doom-hide-modeline-mode' is active. Don't
|
||
set this directly. Bind it in `let' instead.")
|
||
(defvar-local doom--old-modeline-format nil
|
||
"The old modeline format, so `doom-hide-modeline-mode' can revert when it's
|
||
disabled.")
|
||
(define-minor-mode doom-hide-modeline-mode
|
||
"Minor mode to hide the mode-line in the current buffer."
|
||
:init-value nil
|
||
:global nil
|
||
(if doom-hide-modeline-mode
|
||
(setq doom--old-modeline-format mode-line-format
|
||
mode-line-format doom--modeline-format)
|
||
(setq mode-line-format doom--old-modeline-format
|
||
doom--old-modeline-format nil))
|
||
(force-mode-line-update))
|
||
;; Ensure major-mode or theme changes don't overwrite these variables
|
||
(put 'doom--modeline-format 'permanent-local t)
|
||
(put 'doom--old-modeline-format 'permanent-local t)
|
||
(put 'doom-hide-modeline-mode 'permanent-local t)
|
||
|
||
(defun doom|hide-modeline-mode-reset ()
|
||
"Sometimes, a major-mode is activated after `doom-hide-modeline-mode' is
|
||
activated, thus disabling it (because changing major modes invokes
|
||
`kill-all-local-variables' and specifically seems to kill `mode-line-format's
|
||
local value, whether or not it's permanent-local. Therefore, we cycle
|
||
`doom-hide-modeline-mode' to fix this."
|
||
(when doom-hide-modeline-mode
|
||
(doom-hide-modeline-mode -1)
|
||
(doom-hide-modeline-mode +1)))
|
||
(add-hook 'after-change-major-mode-hook #'doom|hide-modeline-mode-reset)
|
||
|
||
;; no modeline in completion popups
|
||
(add-hook 'completion-list-mode-hook #'doom-hide-modeline-mode)
|
||
|
||
;; undo/redo changes to Emacs' window layout
|
||
(defvar winner-dont-bind-my-keys t) ; I'll bind keys myself
|
||
(require 'winner)
|
||
(add-hook 'window-setup-hook #'winner-mode)
|
||
|
||
;; highlight matching delimiters
|
||
(setq show-paren-delay 0.1
|
||
show-paren-highlight-openparen t
|
||
show-paren-when-point-inside-paren t)
|
||
(show-paren-mode +1)
|
||
|
||
|
||
;;
|
||
;; Bootstrap
|
||
;;
|
||
|
||
(tooltip-mode -1) ; relegate tooltips to echo area only
|
||
(menu-bar-mode -1)
|
||
(when (display-graphic-p)
|
||
(scroll-bar-mode -1)
|
||
(tool-bar-mode -1)
|
||
;; buffer name in frame title
|
||
(setq-default frame-title-format '("DOOM Emacs"))
|
||
;; standardize fringe width
|
||
(push (cons 'left-fringe doom-ui-fringe-size) default-frame-alist)
|
||
(push (cons 'right-fringe doom-ui-fringe-size) default-frame-alist)
|
||
;; no fringe in the minibuffer
|
||
(add-hook! (emacs-startup minibuffer-setup)
|
||
(set-window-fringes (minibuffer-window) 0 0 nil)))
|
||
|
||
|
||
;;
|
||
;; Plugins
|
||
;;
|
||
|
||
(def-package! hideshow ; built-in
|
||
:commands (hs-minor-mode hs-toggle-hiding hs-already-hidden-p)
|
||
:config
|
||
(setq hs-hide-comments-when-hiding-all nil))
|
||
|
||
;; Show uninterrupted indentation markers with some whitespace voodoo.
|
||
(def-package! highlight-indentation
|
||
:commands (highlight-indentation-mode highlight-indentation-current-column-mode)
|
||
:config
|
||
(defun doom|inject-trailing-whitespace (&optional start end)
|
||
"The opposite of `delete-trailing-whitespace'. Injects whitespace into
|
||
buffer so that `highlight-indentation-mode' will display uninterrupted indent
|
||
markers. This whitespace is stripped out on save, as not to affect the resulting
|
||
file."
|
||
(interactive (progn (barf-if-buffer-read-only)
|
||
(if (use-region-p)
|
||
(list (region-beginning) (region-end))
|
||
(list nil nil))))
|
||
(unless indent-tabs-mode
|
||
(save-match-data
|
||
(save-excursion
|
||
(let ((end-marker (copy-marker (or end (point-max))))
|
||
(start (or start (point-min))))
|
||
(goto-char start)
|
||
(while (and (re-search-forward "^$" end-marker t) (< (point) end-marker))
|
||
(let (line-start line-end next-start next-end)
|
||
(save-excursion
|
||
;; Check previous line indent
|
||
(forward-line -1)
|
||
(setq line-start (point)
|
||
line-end (save-excursion (back-to-indentation) (point)))
|
||
;; Check next line indent
|
||
(forward-line 2)
|
||
(setq next-start (point)
|
||
next-end (save-excursion (back-to-indentation) (point)))
|
||
;; Back to origin
|
||
(forward-line -1)
|
||
;; Adjust indent
|
||
(let* ((line-indent (- line-end line-start))
|
||
(next-indent (- next-end next-start))
|
||
(indent (min line-indent next-indent)))
|
||
(insert (make-string (if (zerop indent) 0 (1+ indent)) ? )))))
|
||
(forward-line 1)))))
|
||
(set-buffer-modified-p nil))
|
||
nil)
|
||
|
||
(add-hook! (highlight-indentation-mode highlight-indentation-current-column-mode)
|
||
(if (or highlight-indentation-mode highlight-indentation-current-column-mode)
|
||
(progn
|
||
(doom|inject-trailing-whitespace)
|
||
(add-hook 'before-save-hook #'delete-trailing-whitespace nil t)
|
||
(add-hook 'after-save-hook #'doom|inject-trailing-whitespace nil t))
|
||
(remove-hook 'before-save-hook #'delete-trailing-whitespace t)
|
||
(remove-hook 'after-save-hook #'doom|inject-trailing-whitespace t)
|
||
(delete-trailing-whitespace))))
|
||
|
||
;; For modes that don't adequately highlight numbers
|
||
(def-package! highlight-numbers :commands highlight-numbers-mode)
|
||
|
||
;; Line highlighting
|
||
(def-package! hl-line ; built-in
|
||
:init
|
||
(add-hook! (linum-mode nlinum-mode) #'hl-line-mode)
|
||
:config
|
||
;; stickiness doesn't play nice with emacs 25+
|
||
(setq hl-line-sticky-flag nil
|
||
global-hl-line-sticky-flag nil)
|
||
|
||
;; acts weird with evil visual mode, so disable it temporarily
|
||
(defun doom|hl-line-off () (hl-line-mode -1))
|
||
(after! evil
|
||
(add-hook! 'hl-line-mode-hook
|
||
(when hl-line-mode
|
||
(add-hook 'evil-visual-state-entry-hook #'doom|hl-line-off nil t)
|
||
(add-hook 'evil-visual-state-exit-hook #'hl-line-mode nil t)))))
|
||
|
||
;; Line number column. A faster (or equivalent, in the worst case) line number
|
||
;; plugin than the built-in `linum'.
|
||
(def-package! nlinum
|
||
:commands nlinum-mode
|
||
:preface
|
||
(defvar linum-format "%3d ")
|
||
(defvar nlinum-format "%4d ")
|
||
:init
|
||
(add-hook! (prog-mode text-mode)
|
||
(unless (eq major-mode 'org-mode)
|
||
(nlinum-mode +1)))
|
||
|
||
:config
|
||
(defun doom*nlinum-flush-all-windows (&rest _)
|
||
"Fix nlinum margins after major UI changes (like a change of font)."
|
||
(dolist (buffer (doom-visible-buffers))
|
||
(with-current-buffer buffer
|
||
(when nlinum-mode (nlinum--flush)))))
|
||
(advice-add #'set-frame-font :after #'doom*nlinum-flush-all-windows)
|
||
|
||
;; A known issue with nlinum is that line numbers disappear over time. These
|
||
;; hooks/advisors will attempt to stave off these glitches.
|
||
(defun doom*nlinum-flush (&optional _ norecord)
|
||
;; norecord check is necessary to prevent infinite recursion in
|
||
;; `select-window'
|
||
(when (and nlinum-mode (not norecord))
|
||
(if _
|
||
(nlinum--flush)
|
||
;; done in two steps to leave current line number highlighting alone
|
||
(nlinum--region (point-min) (line-beginning-position))
|
||
(nlinum--region (line-end-position) (point-max)))))
|
||
;; refresh when switching windows
|
||
(advice-add #'select-window :before #'doom*nlinum-flush)
|
||
(advice-add #'select-window :after #'doom*nlinum-flush)
|
||
;; and when pressing ESC in normal mode
|
||
(add-hook '+evil-esc-hook #'doom*nlinum-flush)
|
||
|
||
;;
|
||
(after! web-mode
|
||
(advice-add #'web-mode-fold-or-unfold :after #'nlinum--flush))
|
||
|
||
;; Optimization: calculate line number column width beforehand
|
||
(add-hook! nlinum-mode
|
||
(setq nlinum--width (length (save-excursion (goto-char (point-max))
|
||
(format-mode-line "%l"))))))
|
||
|
||
;; Helps us distinguish stacked delimiter pairs. Especially in parentheses-drunk
|
||
;; languages like Lisp.
|
||
(def-package! rainbow-delimiters
|
||
:commands rainbow-delimiters-mode
|
||
:config (setq rainbow-delimiters-max-face-count 3)
|
||
:init (add-hook 'lisp-mode-hook #'rainbow-delimiters-mode))
|
||
|
||
;; indicators for empty lines past EOF
|
||
(def-package! vi-tilde-fringe
|
||
:when (display-graphic-p)
|
||
:demand t
|
||
:config (global-vi-tilde-fringe-mode t))
|
||
|
||
;; For a distractions-free-like UI, that dynamically resizes margets and can
|
||
;; center a buffer.
|
||
(def-package! visual-fill-column
|
||
:commands visual-fill-column-mode
|
||
:config
|
||
(setq-default visual-fill-column-center-text nil
|
||
visual-fill-column-width fill-column))
|
||
|
||
|
||
;;
|
||
;; Modeline
|
||
;;
|
||
|
||
(defmacro def-modeline-segment! (name &rest forms)
|
||
"Defines a modeline segment and byte compiles it."
|
||
(declare (indent defun) (doc-string 2))
|
||
(let ((sym (intern (format "doom-modeline-segment--%s" name))))
|
||
`(progn
|
||
(defun ,sym () ,@forms)
|
||
,(unless (bound-and-true-p byte-compile-current-file)
|
||
`(let (byte-compile-warnings)
|
||
(byte-compile #',sym))))))
|
||
|
||
(defsubst doom--prepare-modeline-segments (segments)
|
||
(let (segs)
|
||
(dolist (seg segments (nreverse segs))
|
||
(push (if (stringp seg)
|
||
seg
|
||
(list (intern (format "doom-modeline-segment--%s" (symbol-name seg)))))
|
||
segs))))
|
||
|
||
(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)))
|
||
(list lhs
|
||
(propertize
|
||
" " 'display
|
||
`((space :align-to (- (+ right right-fringe right-margin)
|
||
,(+ 1 (string-width (format-mode-line rhs)))))))
|
||
rhs)))
|
||
,(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."
|
||
(let ((modeline (doom-modeline key)))
|
||
(when modeline
|
||
(setf (if default
|
||
(default-value 'mode-line-format)
|
||
(buffer-local-value 'mode-line-format (current-buffer)))
|
||
modeline))))
|
||
|
||
(provide 'core-ui)
|
||
;;; core-ui.el ends here
|