Make def-setting! behave more like defmacro

set! used to aggressively evaluate its arguments (at expansion-time),
even if placed inside an after! block. This causes unavoidable errors if
those arguments use functions/variables that don't exist yet.

Fixes #112
This commit is contained in:
Henrik Lissner 2017-06-19 00:22:04 +02:00
parent 27cbd36b69
commit 928812da8a
No known key found for this signature in database
GPG key ID: 5F6C0EA160557395
12 changed files with 104 additions and 107 deletions

View file

@ -64,7 +64,6 @@
+ Add README.org's with working babel blocks to modules. + Add README.org's with working babel blocks to modules.
+ Document best practices for customizing DOOM emacs. + Document best practices for customizing DOOM emacs.
+ =ui/doom-modeline= fix hardcoded spacing in between segments. + =ui/doom-modeline= fix hardcoded spacing in between segments.
+ Fix ~def-setting!~ to act more like ~defmacro~, and not aggressively evaluate its arguments on expansion (and update all ~set!~ calls).
+ Rewrite main README.md to include more information about setting up, customizing, and troubleshooting DOOM Emacs. + Rewrite main README.md to include more information about setting up, customizing, and troubleshooting DOOM Emacs.
* Unreleased (master) * Unreleased (master)
@ -79,6 +78,7 @@
+ Fix =private/<user-login-name>/init.el= not being auto-loaded when the user's private module is absent in the root init.el file. + Fix =private/<user-login-name>/init.el= not being auto-loaded when the user's private module is absent in the root init.el file.
+ Improve error handling across the board. Emacs should now report more helpful errors. Catastrophic errors will be less likely to inhibit later modules from being loaded. + Improve error handling across the board. Emacs should now report more helpful errors. Catastrophic errors will be less likely to inhibit later modules from being loaded.
+ Unit-tests have been moved to their respective modules (and =core/test/=). + Unit-tests have been moved to their respective modules (and =core/test/=).
+ Fix ~def-setting!~ to act more like ~defmacro~; don't aggressively evaluate its arguments on expansion.
+ =core-ui= + =core-ui=
+ Add quit confirmation when trying to close a frame that contains real buffers. + Add quit confirmation when trying to close a frame that contains real buffers.
+ Fix quit confirmations for clients connected to ~emacs --daemon~ with ~emacsclient~. + Fix quit confirmations for clients connected to ~emacs --daemon~ with ~emacsclient~.
@ -96,6 +96,13 @@
+ =feature= + =feature=
+ =feature/evil= + =feature/evil=
+ Remove =goto-last-change=, which conflicts with =goto-chg=, which is a dependency of evil (that does the exact same thing, but is what evil uses). + Remove =goto-last-change=, which conflicts with =goto-chg=, which is a dependency of evil (that does the exact same thing, but is what evil uses).
+ =feature/jump=
+ Remove ~:xref-backend~ setting (replaced with ~:jump~).
+ Add ~:jump MAJOR-MODE &rest PLIST~ setting, which recognizes four properties (that accept functions/commands):
+ ~:definition~: jumps to the definition of the symbol under point.
+ ~:references~: lists all references of the symbol at point and lets you jump to them.
+ ~:documentation~: shows documentation for the symbol at point.
+ ~:xref-backend~: a function that serves as an xref backend; this replaces ~:definition~ and ~:references~.
+ =ui= + =ui=
+ =ui/doom= + =ui/doom=
+ Vastly improve daemon and terminal support for doom-themes by reloading the theme when a new client is attached, or new terminal/daemon frame is created. This prevents incorrect colors from bleeding across face class barriers. + Vastly improve daemon and terminal support for doom-themes by reloading the theme when a new client is attached, or new terminal/daemon frame is created. This prevents incorrect colors from bleeding across face class barriers.

View file

@ -138,16 +138,13 @@ fundamental-mode) for performance sake."
:init :init
(def-setting! :editorconfig (action value) (def-setting! :editorconfig (action value)
":add or :remove an entry in `editorconfig-indentation-alist'." ":add or :remove an entry in `editorconfig-indentation-alist'."
`(after! editorconfig (cond ((eq action :add)
,(cond ((eq action :add) `(push ,value editorconfig-indentation-alist))
`(push ',value editorconfig-indentation-alist)) ((eq action :remove)
((eq action :remove) `(setq editorconfig-indentation-alist
(unless (symbolp value) (assq-delete-all ,value editorconfig-indentation-alist)))
(error "%s is not a valid major-mode in editorconfig-indentation-alist" value)) (t (error "%s is an invalid action for :editorconfig"
`(setq editorconfig-indentation-alist action))))
(delq (assq ',value editorconfig-indentation-alist)
editorconfig-indentation-alist)))
(t (error "%s is an invalid action for :editorconfig" action)))))
:config :config
(add-hook 'doom-init-hook #'editorconfig-mode) (add-hook 'doom-init-hook #'editorconfig-mode)

View file

@ -226,20 +226,21 @@ executed when called with `set!'. FORMS are not evaluated until `set!' calls it.
(declare (indent defun) (doc-string 3)) (declare (indent defun) (doc-string 3))
(unless (keywordp keyword) (unless (keywordp keyword)
(error "Not a valid property name: %s" keyword)) (error "Not a valid property name: %s" keyword))
`(progn (let ((fn (intern (format "doom-setting--setter%s" keyword))))
(defun ,(intern (format "doom-setting--setter%s" keyword)) ,arglist `(progn
,docstring (defun ,fn ,arglist
,@forms) ,docstring
(cl-pushnew ,keyword doom-settings))) ,@forms)
(cl-pushnew ',(cons keyword fn) doom-settings :test #'eq :key #'car))))
(defmacro set! (keyword &rest values) (defmacro set! (keyword &rest values)
"Set an option defined by `def-setting!'. Skip if doesn't exist." "Set an option defined by `def-setting!'. Skip if doesn't exist."
(declare (indent defun)) (declare (indent defun))
(unless values (unless values
(error "Empty set! for %s" keyword)) (error "Empty set! for %s" keyword))
(let ((fn (intern (format "doom-setting--setter%s" keyword)))) (let ((fn (cdr (assq keyword doom-settings))))
(if (functionp fn) (if fn
(apply fn (eval `(list ,@values))) (apply fn values)
(when doom-debug-mode (when doom-debug-mode
(message "No setting found for %s" keyword))))) (message "No setting found for %s" keyword)))))

View file

@ -38,9 +38,9 @@ is enabled/disabled.'")
(def-setting! :popup (&rest rules) (def-setting! :popup (&rest rules)
"Prepend a new popup rule to `shackle-rules'." "Prepend a new popup rule to `shackle-rules'."
(if (cl-every #'listp rules) (if (cl-every #'listp (mapcar #'doom-unquote rules))
`(setq shackle-rules (nconc ',rules shackle-rules)) `(setq shackle-rules (nconc (list ,@rules) shackle-rules))
`(push ',rules shackle-rules))) `(push (list ,@rules) shackle-rules)))
;; ;;

View file

@ -12,20 +12,27 @@
;; Config ;; Config
;; ;;
(defvar +email--accounts nil) (def-setting! :email (label letvars &optional default-p)
"Registers an email address for mu4e. The LABEL is a string. LETVARS are a
list of cons cells (VARIABLE . VALUE) -- you may want to modify:
(def-setting! :email (label letvars &optional default) + `user-full-name' (this or the global `user-full-name' is required)
"Registers an email address for mu4e." + `user-mail-address' (required)
(let ((name (or (cdr (assq 'user-full-name letvars)) user-full-name)) + `smtpmail-smtp-user' (required for sending mail from Emacs)
(address (cdr (assq 'user-mail-address letvars))))
(dolist (var letvars) OPTIONAL:
(let ((val (cdr var))) + `mu4e-sent-folder'
(when (and (stringp val) (string-match-p "%s" val)) + `mu4e-drafts-folder'
(setcdr var (format val label))))) + `mu4e-trash-folder'
`(progn + `mu4e-refile-folder'
(push ',(cons label letvars) +email--accounts) + `mu4e-compose-signature'
,(when address
`(add-to-list 'mu4e-user-mail-address-list ,address)) DEFAULT-P is a boolean. If non-nil, it marks that email account as the
default/fallback account."
`(after! mu4e
(let ((account-vars ,letvars))
(when-let (address (cdr (assq 'user-mail-address account-vars)))
(cl-pushnew address mu4e-user-mail-address-list :test #'equal))
(let ((context (make-mu4e-context (let ((context (make-mu4e-context
:name ,label :name ,label
:enter-func (lambda () (mu4e-message "Switched to %s" ,label)) :enter-func (lambda () (mu4e-message "Switched to %s" ,label))
@ -33,10 +40,11 @@
:match-func :match-func
(lambda (msg) (lambda (msg)
(when msg (when msg
(string-prefix-p (format "/%s" ,label) (mu4e-message-field msg :maildir)))) (string-prefix-p (format "/%s" ,label)
:vars ',letvars))) (mu4e-message-field msg :maildir))))
:vars ,letvars)))
(push context mu4e-contexts) (push context mu4e-contexts)
,(when default ,(when default-p
`(setq-default mu4e-context-current context)))))) `(setq-default mu4e-context-current context))))))

View file

@ -15,9 +15,6 @@
(defvar +irc-notifications-watch-strings nil (defvar +irc-notifications-watch-strings nil
"TODO") "TODO")
(defvar +irc-connections nil
"A list of connections set with :irc. W")
(defvar +irc-defer-notifications nil (defvar +irc-defer-notifications nil
"How long to defer enabling notifications, in seconds (e.g. 5min = 300). "How long to defer enabling notifications, in seconds (e.g. 5min = 300).
Useful for ZNC users who want to avoid the deluge of notifications during buffer Useful for ZNC users who want to avoid the deluge of notifications during buffer
@ -25,8 +22,9 @@ playback.")
(def-setting! :irc (server letvars) (def-setting! :irc (server letvars)
"Registers an irc server for circe." "Registers an irc server for circe."
`(cl-pushnew (cons ,server ,letvars) +irc-connections `(after! circe
:test #'equal :key #'car)) (cl-pushnew (cons ,server ,letvars) circe-network-options
:test #'equal :key #'car)))
(defvar +irc--defer-timer nil) (defvar +irc--defer-timer nil)
@ -39,10 +37,6 @@ playback.")
:commands (circe circe-server-buffers) :commands (circe circe-server-buffers)
:init (setq circe-network-defaults nil) :init (setq circe-network-defaults nil)
:config :config
;; change hands
(setq circe-network-options +irc-connections)
(defvaralias '+irc-connections 'circe-network-options)
(defsubst +irc--pad (left right) (defsubst +irc--pad (left right)
(format (format "%%%ds | %%s" +irc-left-padding) (format (format "%%%ds | %%s" +irc-left-padding)
(concat "*** " left) right)) (concat "*** " left) right))

View file

@ -2,17 +2,15 @@
(def-setting! :company-backend (modes backends) (def-setting! :company-backend (modes backends)
"Register company BACKENDS to MODES." "Register company BACKENDS to MODES."
(let* ((modes (if (listp modes) modes (list modes))) (let* ((modes (doom-enlist (doom-unquote modes)))
(backends (if (listp backends) backends (list backends))) (backends (doom-enlist (doom-unquote backends)))
(def-name (intern (format "doom--init-company-%s" (def-name (intern (format "doom--init-company-%s"
(mapconcat #'identity (mapcar #'symbol-name modes) "-"))))) (mapconcat #'symbol-name modes "-")))))
;; TODO more type checks
`(prog1 `(prog1
(defun ,def-name () (defun ,def-name ()
(when (memq major-mode ',modes) (when (memq major-mode ,modes)
(require 'company) (require 'company)
(unless (member ',backends company-backends) (cl-pushnew ,backends company-backends :test #'equal)))
(setq-local company-backends (append '((,@backends)) company-backends)))))
(add-hook! ,modes #',def-name)))) (add-hook! ,modes #',def-name))))

View file

@ -15,11 +15,12 @@ PLIST accepts the following properties:
:when FORM A predicate to determine if the builder is appropriate for this :when FORM A predicate to determine if the builder is appropriate for this
buffer." buffer."
`(dolist (mode ',(if (listp modes) modes (list modes)) +eval-builders) `(dolist (mode ',(doom-enlist (doom-unquote modes)) +eval-builders)
(unless (assq mode +eval-builders) (unless (assq mode +eval-builders)
(push (list mode) +eval-builders)) (push (list mode) +eval-builders))
(push (cons ',name (append (list :fn #',fn) ',plist)) (cl-pushnew (cons ,name (append (list :fn ,fn) (list ,@plist)))
(cdr (assq mode +eval-builders))))) (cdr (assq mode +eval-builders))
:test #'eq :key #'car)))
;; ;;
@ -35,9 +36,9 @@ PLIST accepts the following properties:
:init-value nil) :init-value nil)
(def-setting! :repl (mode command) (def-setting! :repl (mode command)
"Define a REPL for a mode. MODE is a major mode and COMMAND is a function that "Define a REPL for a mode. MODE is a major mode symbol and COMMAND is a
invokes the repl. Takes the same arguements as `rtog/add-repl'." function that creates and returns the REPL buffer."
`(push ',(cons mode command) +eval-repls)) `(push (cons ,mode ,command) +eval-repls))
(set! :popup (set! :popup
'(:custom (lambda (b &rest _) (buffer-local-value '+eval-repl-mode b))) '(:custom (lambda (b &rest _) (buffer-local-value '+eval-repl-mode b)))
@ -67,19 +68,20 @@ invokes the repl. Takes the same arguements as `rtog/add-repl'."
(quickrun-add-command MODE COMMAND :mode MODE). (quickrun-add-command MODE COMMAND :mode MODE).
4. If MODE is not a string and COMMANd is a symbol, add it to 4. If MODE is not a string and COMMANd is a symbol, add it to
`+eval-runners-alist', which is used by `+eval/region'." `+eval-runners-alist', which is used by `+eval/region'."
(cond ((symbolp command) (let ((command (doom-unquote command)))
`(push ',(cons mode command) +eval-runners-alist)) (cond ((symbolp command)
((stringp command) `(push (cons ,mode ',command) +eval-runners-alist))
`(after! quickrun ((stringp command)
(push ',(cons mode command) `(after! quickrun
,(if (stringp mode) (push (cons ,mode ',command)
'quickrun-file-alist ,(if (stringp mode)
'quickrun--major-mode-alist)))) 'quickrun-file-alist
((listp command) 'quickrun--major-mode-alist))))
`(after! quickrun ((listp command)
(quickrun-add-command `(after! quickrun
,(symbol-name mode) (quickrun-add-command
',command :mode ',mode))))) ,(symbol-name (doom-unquote mode))
',command :mode ,mode))))))
(def-package! quickrun (def-package! quickrun
:commands (quickrun :commands (quickrun

View file

@ -3,19 +3,14 @@
;; I'm a vimmer at heart. Its modal philosophy suits me better, and this module ;; I'm a vimmer at heart. Its modal philosophy suits me better, and this module
;; strives to make Emacs a much better vim than vim was. ;; strives to make Emacs a much better vim than vim was.
(def-setting! :evil-state (&rest mode-state-list) (def-setting! :evil-state (modes state)
"Set the initialize STATE of MODE using `evil-set-initial-state'." "Set the initialize STATE of MODE using `evil-set-initial-state'."
(if (cl-every #'listp mode-state-list) (let ((unquoted-modes (doom-unquote modes)))
`(progn (if (listp unquoted-modes)
,@(let (forms) `(progn
(dolist (it mode-state-list (nreverse forms)) ,@(cl-loop for mode in unquoted-modes
(unless (consp it) collect `(evil-set-initial-state ',mode ,state)))
(error ":evil-state expected cons cells, got %s" it)) `(evil-set-initial-state ,modes ,state))))
(push `(evil-set-initial-state ',(car it) ',(cdr it)) forms))))
(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))))
;; ;;

View file

@ -11,6 +11,5 @@
'("*vc-change-log*" :size 15) '("*vc-change-log*" :size 15)
'(vc-annotate-mode :same t)) '(vc-annotate-mode :same t))
(set! :evil-state (set! :evil-state 'vc-annotate-mode 'normal)
'(vc-annotate-mode . normal) (set! :evil-state 'vc-git-log-view-mode 'normal))
'(vc-git-log-view-mode . normal)))

View file

@ -22,15 +22,15 @@
"Declare :words (list of strings) or :chars (lists of chars) in MODES that "Declare :words (list of strings) or :chars (lists of chars) in MODES that
trigger electric indentation." trigger electric indentation."
(declare (indent 1)) (declare (indent 1))
(let ((modes (if (listp modes) modes (list modes))) (let ((modes (doom-enlist (doom-unquote modes)))
(chars (plist-get plist :chars)) (chars (doom-unquote (plist-get plist :chars)))
(words (plist-get plist :words))) (words (doom-unquote (plist-get plist :words))))
(when (or chars words) (when (or chars words)
(let ((fn-name (intern (format "doom--electric-%s" (string-join (mapcar #'symbol-name modes) "-"))))) (let ((fn-name (intern (format "doom--init-electric-%s" (mapconcat #'symbol-name modes "-")))))
`(progn `(progn
(defun ,fn-name () (defun ,fn-name ()
(electric-indent-local-mode +1) (electric-indent-local-mode +1)
,(if chars `(setq electric-indent-chars ',chars)) ,@(if chars `((setq electric-indent-chars ',chars)))
,(if words `(setq doom-electric-indent-words ',words))) ,@(if words `((setq doom-electric-indent-words ',words))))
(add-hook! ,modes #',fn-name)))))) (add-hook! ,modes #',fn-name))))))

View file

@ -9,17 +9,13 @@
(def-setting! :rotate (modes &rest plist) (def-setting! :rotate (modes &rest plist)
"Declare :symbols, :words or :patterns that `rotate-text' will cycle through." "Declare :symbols, :words or :patterns that `rotate-text' will cycle through."
(declare (indent 1)) (declare (indent 1))
(let ((modes (if (listp modes) modes (list modes))) (let* ((modes (doom-enlist (doom-unquote modes)))
(symbols (plist-get plist :symbols)) (fn-name (intern (format "doom--rotate-%s" (mapconcat #'symbol-name modes "-")))))
(words (plist-get plist :words)) `(progn
(patterns (plist-get plist :patterns))) (defun ,fn-name ()
(when (or symbols words patterns) (let ((plist (list ,@plist)))
(let ((fn-name (intern (format "doom--rotate-%s" (string-join (mapcar #'symbol-name modes) "-"))))) (setq rotate-text-local-symbols (plist-get plist :symbols)
`(progn rotate-text-local-words (plist-get plist :words)
(defun ,fn-name () rotate-text-local-patterns (plist-get plist :patterns))))
(require 'rotate-text) (add-hook! ,modes #',fn-name))))
,(if symbols `(setq rotate-text-local-symbols ',symbols))
,(if words `(setq rotate-text-local-words ',words))
,(if patterns `(setq rotate-text-local-patterns ',patterns)))
(add-hook! ,modes #',fn-name))))))