Rethink map! macro; replace :local=>:L, :defer=>:map* (addresses #19)

This commit is contained in:
Henrik Lissner 2017-01-02 20:59:28 -05:00
parent 5536ab887d
commit e1000fcfc5
8 changed files with 174 additions and 143 deletions

View file

@ -193,13 +193,14 @@ Examples:
(evil-ex-define-cmd cmd fn))
;; Register keywords for proper indentation (see `map!')
(put ':prefix 'lisp-indent-function 'defun)
(put ':map 'lisp-indent-function 'defun)
(put ':after 'lisp-indent-function 'defun)
(put ':when 'lisp-indent-function 'defun)
(put ':unless 'lisp-indent-function 'defun)
(put ':leader 'lisp-indent-function 'defun)
(put ':localleader 'lisp-indent-function 'defun)
(put ':prefix 'lisp-indent-function 'defun)
(put ':map 'lisp-indent-function 'defun)
(put ':map* 'lisp-indent-function 'defun)
(put ':after 'lisp-indent-function 'defun)
(put ':when 'lisp-indent-function 'defun)
(put ':unless 'lisp-indent-function 'defun)
(put ':leader 'lisp-indent-function 'defun)
(put ':localleader 'lisp-indent-function 'defun)
(defmacro map! (&rest rest)
"A nightmare of a key-binding macro that will use `evil-define-key',
@ -207,6 +208,11 @@ Examples:
plist key flags. It was designed to make binding multiple keys more concise,
like in vim.
Yes, it tries to do too much. Yes, I only did it to make the \"frontend\" config
that little bit more concise. Yes, I could simply have used the above functions.
But it takes a little insanity to custom write your own emacs.d, so what else
were you expecting?
States
:n normal
:v visual
@ -215,19 +221,22 @@ States
:o operator
:m motion
:r replace
:L local
These can be combined (order doesn't matter), e.g. :nvi will apply to
normal, visual and insert mode. The state resets after the following
key=>def pair.
Capitalize the state flag to make it a local binding.
If omitted, the keybind will be defined globally.
Flags
:unset [KEY] ; unset key
:nodefer ; don't use `evil-delay' for future keybinds
(:map [KEYMAP] [...]) ; apply all inner keybinds to KEYMAP
(:prefix [PREFIX] [...]) ; assign prefix to all inner keybindings
(:after [FEATURE] [...]) ; apply keybinds when [FEATURE] loads
:unset [KEY] ; unset key
(:map [KEYMAP] [...]) ; apply inner keybinds to KEYMAP
(:map* [KEYMAP] [...]) ; apply inner keybinds to KEYMAP (deferred)
(:prefix [PREFIX] [...]) ; assign prefix to all inner keybindings
(:after [FEATURE] [...]) ; apply keybinds when [FEATURE] loads
Conditional keybinds
(:when [CONDITION] [...])
@ -242,8 +251,7 @@ Example
(:when IS-MAC
:n \"M-s\" 'some-fn
:i \"M-o\" (lambda (interactive) (message \"Hi\"))))"
(let ((i 0)
(keymaps (if (boundp 'keymaps) keymaps))
(let ((keymaps (if (boundp 'keymaps) keymaps))
(state-map '(("n" . normal)
("v" . visual)
("i" . insert)
@ -252,73 +260,89 @@ Example
("m" . motion)
("r" . replace)))
(prefix (if (boundp 'prefix) prefix))
(nodefer (if (boundp 'nodefer) nodefer))
key def states forms)
(defer (if (boundp 'defer) defer))
local key def states forms)
(while rest
(setq key (pop rest))
(push
(reverse
(cond ((listp key) ; it's a sub exp
`(,(macroexpand `(map! ,@key))))
(cond
;; it's a sub expr
((listp key)
`(,(macroexpand `(map! ,@key))))
((keywordp key)
(when (memq key '(:leader :localleader))
(push (cond ((eq key :leader)
doom-leader)
((eq key :localleader)
doom-localleader))
rest)
(setq key :prefix))
(pcase key
(:prefix (setq prefix (concat prefix (kbd (pop rest)))) nil)
(:map (setq keymaps (-list (pop rest))) nil)
(:nodefer (setq nodefer t) nil)
(:unset `(,(macroexpand `(map! ,(kbd (pop rest)) nil))))
(:after (prog1 `((after! ,(pop rest) ,(macroexpand `(map! ,@rest)))) (setq rest '())))
(:when (prog1 `((if ,(pop rest) ,(macroexpand `(map! ,@rest)))) (setq rest '())))
(:unless (prog1 `((if (not ,(pop rest)) ,(macroexpand `(map! ,@rest)))) (setq rest '())))
(otherwise ; might be a state prefix
(mapc (lambda (letter)
(cond ((assoc letter state-map)
(push (cdr (assoc letter state-map)) states))
(t (user-error "Invalid mode prefix %s in key %s" letter key))))
(split-string (substring (symbol-name key) 1) "" t))
(unless states
(user-error "Unrecognized keyword %s" key)) nil)))
;; it's a flag
((keywordp key)
(when (memq key '(:leader :localleader))
(push (cond ((eq key :leader)
doom-leader)
((eq key :localleader)
doom-localleader))
rest)
(setq key :prefix))
(pcase key
(:prefix (setq prefix (concat prefix (kbd (pop rest)))) nil)
(:map (setq keymaps (-list (pop rest))) nil)
(:map* (setq defer t keymaps (-list (pop rest))) nil)
(:unset `(,(macroexpand `(map! ,(kbd (pop rest)) nil))))
(:after (prog1 `((after! ,(pop rest) ,(macroexpand `(map! ,@rest)))) (setq rest '())))
(:when (prog1 `((if ,(pop rest) ,(macroexpand `(map! ,@rest)))) (setq rest '())))
(:unless (prog1 `((if (not ,(pop rest)) ,(macroexpand `(map! ,@rest)))) (setq rest '())))
(otherwise ; might be a state prefix
(mapc (lambda (letter)
(cond ((assoc letter state-map)
(push (cdr (assoc letter state-map)) states))
((string= letter "L")
(setq local t))
(t (user-error "Invalid mode prefix %s in key %s" letter key))))
(split-string (substring (symbol-name key) 1) "" t))
(unless states
(user-error "Unrecognized keyword %s" key))
(when (assoc "L" states)
(cond ((= (length states) 1)
(user-error "local keybinding for %s must accompany another state" key))
((> (length keymaps) 0)
(user-error "local keybinding for %s cannot accompany a keymap" key))))
nil)))
;; It's a key-def pair
((or (stringp key)
(characterp key)
(vectorp key))
(when (stringp key)
(setq key (kbd key)))
(when prefix
(setq key (cond ((vectorp key) (vconcat prefix key))
(t (concat prefix key)))))
(unless (> (length rest) 0)
(user-error "Map has no definition for %s" key))
(setq def (pop rest))
(let (out-forms)
(cond ((and keymaps states)
(mapc (lambda (keymap)
(push `(,(if nodefer 'evil-define-key* 'evil-define-key)
',states ,keymap ,key ,def)
out-forms))
keymaps))
(keymaps
(mapc (lambda (keymap) (push `(define-key ,keymap ,key ,def) out-forms))
keymaps))
(states
(mapc (lambda (state) (push `(define-key (evil-state-property ',state :keymap t) ,key ,def)
out-forms))
states))
(t (push `(global-set-key ,key ,def) out-forms)))
(setq states '())
out-forms))
;; It's a key-def pair
((or (stringp key)
(characterp key)
(vectorp key))
(when (stringp key)
(setq key (kbd key)))
(when prefix
(setq key (cond ((vectorp key) (vconcat prefix key))
(t (concat prefix key)))))
(unless (> (length rest) 0)
(user-error "Map has no definition for %s" key))
(setq def (pop rest))
(let (out-forms)
(cond ((and keymaps states)
(mapc (lambda (keymap)
(push `(,(if defer 'evil-define-key 'evil-define-key*)
',states ,keymap ,key ,def)
out-forms))
keymaps))
(keymaps
(mapc (lambda (keymap) (push `(define-key ,keymap ,key ,def) out-forms))
keymaps))
(states
(mapc (lambda (state)
(push `(define-key
(evil-state-property ',state ,(if local :local-keymap :keymap) t)
,key ,def)
out-forms))
states))
(t (push `(,(if local 'local-set-key 'global-set-key)
,key ,def)
out-forms)))
(setq states '()
local nil)
out-forms))
(t (user-error "Invalid key %s" key))))
forms)
(setq i (1+ i)))
(t (user-error "Invalid key %s" key))))
forms))
`(progn ,@(apply #'nconc (delete nil (delete (list nil) (reverse forms))))))))
(defmacro def-repeat! (command next-func prev-func)

View file

@ -320,8 +320,8 @@
:i "<C-tab>" 'indent-for-tab-command
:i "<A-tab>" (λ! (insert "\t"))
;; No dumb-tab for lisp
(:map lisp-mode-map :i [remap doom/dumb-indent] 'indent-for-tab-command)
(:map emacs-lisp-mode-map :i [remap doom/dumb-indent] 'indent-for-tab-command)
(:map* lisp-mode-map :i [remap doom/dumb-indent] 'indent-for-tab-command)
(:map* emacs-lisp-mode-map :i [remap doom/dumb-indent] 'indent-for-tab-command)
;; Highjacks space/backspace to:
;; a) eat spaces on either side of the cursor, if present ( | ) -> (|)
;; b) allow backspace to delete space-indented blocks intelligently
@ -344,15 +344,15 @@
:i "<C-up>" 'smart-up
:i "<C-down>" 'smart-down
;; Fix emacs motion keys
:i "A-b" 'evil-backward-word-begin
:i "A-w" 'evil-forward-word-begin
:i "A-e" 'evil-forward-word-end
:i "A-b" 'evil-backward-word-begin
:i "A-w" 'evil-forward-word-begin
:i "A-e" 'evil-forward-word-end
;; Textmate-esque insert-line before/after
:i "<M-return>" 'evil-open-below
:i "<S-M-return>" 'evil-open-above
:i [M-return] 'evil-open-below
:i [S-M-return] 'evil-open-above
;; insert lines in-place)
:n "<M-return>" (λ! (save-excursion (evil-insert-newline-below)))
:n "<S-M-return>" (λ! (save-excursion (evil-insert-newline-above)))
:n [M-return] (λ! (save-excursion (evil-insert-newline-below)))
:n [S-M-return] (λ! (save-excursion (evil-insert-newline-above)))
;; Make ESC quit all the things
(:map (minibuffer-local-map
minibuffer-local-ns-map
@ -378,20 +378,19 @@
"h" nil
"g" nil)))
;; Fix certain keys in the terminal
(unless window-system
(map! :map key-translation-map
"TAB" [tab]))
;; Common unicode characters
(map! :map key-translation-map
"A-o" (kbd "ø")
"A-O" (kbd "Ø")
"A--" (kbd "")
"A-_" (kbd "")
"A-8" (kbd "")
"A-*" (kbd "°")
"A-p" (kbd "π"))
;; Fix certain keys in the terminal
(:unless window-system "TAB" [tab])
;; Common unicode characters
:i "A-o" (kbd "ø")
:i "A-O" (kbd "Ø")
:i "A--" (kbd "")
:i "A-_" (kbd "")
:i "A-8" (kbd "")
:i "A-*" (kbd "°")
:i "A-p" (kbd "π"))
(provide 'core-editor)
;;; core-editor.el ends here

View file

@ -23,22 +23,30 @@
:config
(defvar helm-global-prompt " ")
(map! :map helm-map
"C-S-n" 'helm-next-source
"C-S-p" 'helm-previous-source
"C-u" 'helm-delete-minibuffer-contents
"C-w" 'backward-kill-word
"M-v" 'clipboard-yank
"C-r" 'evil-paste-from-register ; Evil registers in helm! Glorious!
"C-b" 'backward-word
"<left>" 'backward-char
"<right>" 'forward-char
"<escape>" 'helm-keyboard-quit
"ESC" 'helm-keyboard-quit
[escape] 'helm-keyboard-quit
"<tab>" 'helm-execute-persistent-action
:map helm-generic-files-map
:e "ESC" 'helm-keyboard-quit)
(map! "M-x" 'helm-M-x
"A-x" 'helm-M-x
"M-X" 'helm-apropos
"A-X" 'helm-apropos
"A-X" 'helm-apropos
"M-o" 'helm-find-files
(:map helm-map
"C-S-n" 'helm-next-source
"C-S-p" 'helm-previous-source
"C-u" 'helm-delete-minibuffer-contents
"C-w" 'backward-kill-word
"M-v" 'clipboard-yank
"C-r" 'evil-paste-from-register ; Evil registers in helm! Glorious!
"C-b" 'backward-word
"<left>" 'backward-char
"<right>" 'forward-char
"<escape>" 'helm-keyboard-quit
"ESC" 'helm-keyboard-quit
[escape] 'helm-keyboard-quit
"<tab>" 'helm-execute-persistent-action)
(:map* helm-generic-files-map
:e "ESC" 'helm-keyboard-quit))
;;; Popup setup
(def-popup! "\\` ?\\*[hH]elm.*?\\*\\'" :align below :size 14 :select t :regexp t)

View file

@ -64,24 +64,23 @@
;; overridden when the neotree buffer is spawned). So we bind them in a hook.
(add-hook 'neo-after-create-hook 'doom|neotree-init-keymap)
(defun doom|neotree-init-keymap (&rest _)
(map! :map evil-motion-state-local-map
"\\\\" 'evil-window-prev
"ESC ESC" 'neotree-hide
"q" 'neotree-hide
[return] 'neotree-enter
"RET" 'neotree-enter
"<return>" 'neotree-enter
"J" 'neotree-select-next-sibling-node
"K" 'neotree-select-previous-sibling-node
"H" 'neotree-select-up-node
"L" 'neotree-select-down-node
"v" 'neotree-enter-vertical-split
"s" 'neotree-enter-horizontal-split
"c" 'neotree-create-node
"d" 'neotree-delete-node
"C-r" 'neotree-refresh
"r" 'neotree-rename-node
"R" 'neotree-change-root)))
(map! :Lm "\\\\" 'evil-window-prev
:Lm "ESC ESC" 'neotree-hide
:Lm "q" 'neotree-hide
:Lm [return] 'neotree-enter
:Lm "RET" 'neotree-enter
:Lm "<return>" 'neotree-enter
:Lm "J" 'neotree-select-next-sibling-node
:Lm "K" 'neotree-select-previous-sibling-node
:Lm "H" 'neotree-select-up-node
:Lm "L" 'neotree-select-down-node
:Lm "v" 'neotree-enter-vertical-split
:Lm "s" 'neotree-enter-horizontal-split
:Lm "c" 'neotree-create-node
:Lm "d" 'neotree-delete-node
:Lm "C-r" 'neotree-refresh
:Lm "r" 'neotree-rename-node
:Lm "R" 'neotree-change-root)))
(use-package projectile
:config

View file

@ -9,6 +9,11 @@
(sp-with-modes '(css-mode scss-mode less-css-mode stylus-mode)
(sp-local-pair "/*" "*/" :post-handlers '(("[d-3]||\n[i]" "RET") ("| " "SPC"))))
(map! (:map* (css-mode-map scss-mode-map less-css-mode-map)
:n "M-R" 'doom/web-refresh-browser)
(:map* (css-mode-map scss-mode-map less-css-mode-map)
:localleader :nv ";" 'doom/append-semicolon))
(use-package css-mode
:mode "\\.css$"
:init
@ -51,9 +56,6 @@
(push '("scss" "css") projectile-other-file-alist)
(setq scss-compile-at-save nil))
(map! :map (css-mode-map sass-mode-map scss-mode-map)
:n "M-R" 'doom/web-refresh-browser
(:localleader :nv ";" 'doom/append-semicolon))
(provide 'module-css)
;;; module-css.el ends here

View file

@ -3,9 +3,6 @@
(associate! emacs-lisp-mode :match "\\(/Cask\\|\\.\\(el\\|gz\\)\\)$")
(add-hook! emacs-lisp-mode '(eldoc-mode highlight-numbers-mode))
;; Real go-to-definition for elisp
(map! :map emacs-lisp-mode-map :m "gd" 'doom/elisp-find-function-at-pt)
(add-hook 'emacs-lisp-mode-hook 'doom/elisp-init)
(defun doom/elisp-init ()
(def-company-backend! emacs-lisp-mode (elisp yasnippet))
@ -25,6 +22,9 @@
(delq (assq 'emacs-lisp-mode editorconfig-indentation-alist)
editorconfig-indentation-alist))
;; Real go-to-definition for elisp
(map! :map emacs-lisp-mode-map :m "gd" 'doom/elisp-find-function-at-pt)
(remove-hook 'emacs-lisp-mode-hook 'doom/elisp-init))
(add-hook 'emacs-lisp-mode-hook 'doom/elisp-hook)

View file

@ -233,8 +233,7 @@
"Q" 'doom/org-agenda-quit)))
(defun doom|org-keybinds ()
(map! :nodefer
(:map org-mode-map
(map! (:map org-mode-map
"RET" nil
"C-j" nil
"C-k" nil

View file

@ -11,8 +11,7 @@
'(call-interactively 'counsel-find-file)
)))))
(map! :nodefer
[f9] 'what-face
(map! [f9] 'what-face
;; Essential
(:when (featurep 'helm)
"M-x" 'helm-M-x
@ -282,9 +281,10 @@
:nv "<C-tab>" 'aya-create
;; yasnippet
(:map yas-minor-mode-map
:i [tab] 'yas-expand
:v [tab] 'doom/yas-insert-snippet)
(:after yasnippet
(:map yas-minor-mode-map
:i [tab] 'yas-expand
:v [tab] 'doom/yas-insert-snippet))
;; company-mode and vim-like omni-complete
:i "C-SPC" 'doom/company-complete