diff --git a/core/autoload/set.el b/core/autoload/set.el new file mode 100644 index 000000000..dab134ace --- /dev/null +++ b/core/autoload/set.el @@ -0,0 +1,57 @@ +;;; set.el +(provide 'doom-lib-set) + +;; This provides a centralized configuration system that a) won't evaluate its +;; arguments if it doesn't need to (performance), b) won't complain if the +;; setting doesn't exist and c) is more elegant than a bunch of `after!' blocks, +;; which can cause intermittent stuttering in large quantities. I'm a fan of +;; concise, do-what-I-mean front-facing configuration, believe it or not. +;; +;; Plus, it can benefit from byte-compilation. + +;;;###autoload +(defvar doom-settings nil + "An alist of settings, mapping setting keywords to setter functions, which can +be a lambda or symbol.") + +;;;###autoload +(defmacro def-setting! (keyword arglist &optional docstring &rest forms) + "Define a setting macro. Like `defmacro', this should return a form to be +executed when called with `set!'. FORMS are not evaluated until `set!' calls it." + (declare (indent defun) (doc-string 3)) + (unless (keywordp keyword) + (error "Not a valid property name: %s" keyword)) + (let ((sym (intern (format "doom-setting--setter%s" keyword)))) + (setq doom-settings (assq-delete-all keyword doom-settings)) + (prog1 + `(progn + (defun ,sym ,arglist + ,docstring + ,@forms) + (push ',(list keyword + :source (__FILE__) + :docstring docstring + :fn sym) + doom-settings)) + (let (byte-compile-warnings) + (byte-compile sym))))) + +;;;###autoload +(defmacro set! (keyword &rest values) + "Set an option defined by `def-setting!'. Skip if doesn't exist." + (declare (indent defun)) + (unless values + (error "Empty set! for %s" keyword)) + (cond ((not values) + (error "Empty set! for %s" keyword)) + ((not (assq keyword doom-settings)) + (when doom-debug-mode + (warn "No setting found for %s" keyword))) + (t + (let* ((plist (cdr (assq keyword doom-settings))) + (fn (plist-get plist :fn))) + (if fn + (let ((values (eval `(list ,@values)))) + (apply fn values)) + (error "No setting function where one was expected for %s" keyword)))))) + diff --git a/core/core-editor.el b/core/core-editor.el index a8c9bd9be..c06bf3832 100644 --- a/core/core-editor.el +++ b/core/core-editor.el @@ -189,7 +189,7 @@ (package! wgrep :commands (wgrep-setup wgrep-change-to-wgrep-mode) :config - (set! :popup ("^\\*ivy-occur counsel-ag" :size 25 :select t :regexp t)) + (set! :popup "^\\*ivy-occur counsel-ag" :size 25 :select t :regexp t) (setq wgrep-auto-save-buffer t) (advice-add 'wgrep-abort-changes :after 'doom/popup-close) (advice-add 'wgrep-finish-edit :after 'doom/popup-close)) diff --git a/core/core-popups.el b/core/core-popups.el index 8d138ffee..8b1594a34 100644 --- a/core/core-popups.el +++ b/core/core-popups.el @@ -36,18 +36,23 @@ map) "Active keymap in popup windows.") -(def-setting! :popup (&rest rule) +(def-setting! :popup (&rest rules) "Prepend a new popup rule to `shackle-rules'." - (let ((pattern (car rule)) - (plist (cdr rule))) - ;; Align popups by default (error if this doesn't happen) - (unless (plist-member plist :align) - (plist-put plist :align t)) - ;; Select popups by default - (unless (or (plist-member plist :select) - (plist-member plist :noselect)) - (plist-put plist :select t)) - (push (cons pattern plist) shackle-rules))) + (if (not (-all-p 'listp rules)) + `(cl-pushnew ',rules shackle-rules :key 'car :test 'equal) + (let (real-rules) + (dolist (rule rules) + (let ((pattern (car rule)) + (plist (cdr rule))) + ;; Align popups by default (error if this doesn't happen) + (unless (plist-member plist :align) + (plist-put plist :align t)) + ;; Select popups by default + (unless (or (plist-member plist :select) + (plist-member plist :noselect)) + (plist-put plist :select t)) + (push (cons pattern plist) real-rules))) + `(setq shackle-rules (append ',real-rules shackle-rules))))) ;; diff --git a/modules/feature/evil/config.el b/modules/feature/evil/config.el index f658f0701..b98fe19ff 100644 --- a/modules/feature/evil/config.el +++ b/modules/feature/evil/config.el @@ -10,9 +10,19 @@ (defvar +evil-localleader "\\" "The key, used by the `map!' macro for :localleader bindings.") -(def-setting! :evil-state (mode state) +(def-setting! :evil-state (&rest mode-state-list) "Set the initialize STATE of MODE using `evil-set-initial-state'." - (evil-set-initial-state mode state)) + (if (-all-p 'listp mode-state-list) + (macroexp-progn + (--map (let ((argc (length it))) + (unless (= argc 2) + (error ":evil-state sub-lists expect 2 arguments, got %s" argc)) + `(evil-set-initial-state ',(car it) ',(cadr it))) + mode-state-list)) + (let ((argc (length mode-state-list))) + (unless (= argc 2) + (error ":evil-state expected 2 arguments, got %s" argc))) + `(evil-set-initial-state ',(car mode-state-list) ',(cadr mode-state-list)))) ;; @@ -36,8 +46,8 @@ :config (set! :popup - ("*evil-registers*" :size 0.3) - ("*Command Line*" :size 8)) + '("*evil-registers*" :size 0.3) + '("*Command Line*" :size 8)) (evil-mode +1) (evil-select-search-module 'evil-search-module 'evil-search) @@ -374,7 +384,7 @@ if on a delimiter, jump to the matching one (`evilmi-jump-items')." "^#.*#$")) :config - (set! :evil-state (neotree-mode motion)) + (set! :evil-state 'neotree-mode 'motion) ;; Adding keybindings to `neotree-mode-map' wouldn't work for me (they get ;; overridden when the neotree buffer is spawned). So we bind them in a hook. diff --git a/modules/feature/version-control/+git.el b/modules/feature/version-control/+git.el index f5ce0679e..00d26c05d 100644 --- a/modules/feature/version-control/+git.el +++ b/modules/feature/version-control/+git.el @@ -14,7 +14,7 @@ :commands git-gutter-mode :init (add-hook! (text-mode prog-mode conf-mode) 'git-gutter-mode) :config - (set! :popup ("^\\*git-gutter.+\\*$" :regexp t :size 15 :noselect t)) + (set! :popup "^\\*git-gutter.+\\*$" :regexp t :size 15 :noselect t) ;; Update git-gutter on focus (in case I was using git externally) (add-hook 'focus-in-hook 'git-gutter:update-all-windows) @@ -31,7 +31,7 @@ (use-package! magit :commands magit-status :config - (set! :popup ("^\\*magit.+" :regexp t)) + (set! :popup "^\\*magit.+" :regexp t) (after! evil-snipe ;; evil-snipe conflicts with magit (add-hook 'magit-mode-hook 'turn-off-evil-snipe-override-mode))) diff --git a/modules/feature/version-control/config.el b/modules/feature/version-control/config.el index 9b9ce9b70..009da80ba 100644 --- a/modules/feature/version-control/config.el +++ b/modules/feature/version-control/config.el @@ -6,13 +6,13 @@ (after! vc-annotate (set! :popup - ("*vc-diff*" :size 15 :noselect t) - ("*vc-change-log*" :size 15 :select t) - (vc-annotate-mode :same t)) + '("*vc-diff*" :size 15 :noselect t) + '("*vc-change-log*" :size 15 :select t) + '(vc-annotate-mode :same t)) (set! :evil-state - (vc-annotate-mode normal) - (vc-git-log-view-mode normal)) + '(vc-annotate-mode normal) + '(vc-git-log-view-mode normal)) (map! :map vc-annotate-mode-map :n "q" 'kill-this-buffer