;;; emacs init --- an init for emacs -*- lexical-binding: t; -*- ;; by C. Duckworth ;; URL: https://git.acdw.net/emacs ;; Bankruptcy: 9 ;; ;; Everyone is permitted to do whatever they like with this software ;; without limitation. This software comes without any warranty ;; whatsoever, but with two pieces of advice: ;; - Be kind to yourself. ;; - Make good choices. (yoke +emacs (locate-user-emacs-file "lisp/") (require* '+emacs '+window) ;; Settings (setf truncate-string-ellipsis "…" ring-bell-function #'ignore read-file-name-completion-ignore-case t comment-auto-fill-only-comments t password-cache t password-cache-expiry (* 60 60)) ;; "Safe" variables (dolist (var+pred '((browse-url-browser-function ;; All types defined by custom are safe. . (lambda (f) ;; Whooooo boy (memq f (mapcar (lambda (i) (plist-get (cdr i) :value)) (seq-filter (lambda (i) (eq (car i) 'function-item)) (cdr (get 'browse-url-browser-function 'custom-type))))))))) (put (car var+pred) 'safe-local-variable (cdr var+pred))) ;; Keys (define-key* (current-global-map) "C-x C-k" #'kill-current-buffer "C-/" #'undo-only "C-?" #'undo-redo "C-x C-c" #'+save-buffers-quit "M-SPC" #'+cycle-spacing ;; "M-/" #'hippie-expand ; `hippie-completing-read' "M-=" #'count-words "C-x C-b" #'ibuffer "C-x 4 n" #'clone-buffer "S-" #'mouse-set-mark "C-x 0" #'+delete-window-or-bury-buffer "M-j" nil "" nil "M-o" #'other-window|switch-buffer) (define-key* text-mode-map "C-M-k" #'kill-paragraph) ;; Hooks (add-hook 'after-save-hook #'executable-make-buffer-file-executable-if-script-p) (add-hook 'minibuffer-setup-hook #'cursor-intangible-mode) (add-hook 'find-file-not-found-functions #'+auto-create-missing-dirs) (add-hook 'text-mode-hook #'abbrev-mode) (add-hook 'find-file-hook #'+vc-off-when-remote) ;; Advice (add-function :after after-focus-change-function #'+save-some-buffers-debounce) (advice-add 'keyboard-escape-quit :around #'keyboard-escape-quit-keep-window-open) (define-advice keyboard-escape-quit (:around (fn &rest r)) "Don't close quits on `keyboard-escape-quit'." (let ((buffer-quit-function #'ignore)) (apply fn r))) (advice-add 'indent-region :before #'with-region-or-buffer) ;; Themes (load-theme 'modus-operandi) (set-face-attribute 'default nil :family "Comic Code" :height 100) (set-face-attribute 'bold nil :family "Comic Code" :weight 'bold) (set-face-attribute 'variable-pitch nil :family "Comic Code") ;; Modes (winner-mode)) (yoke whitespace nil (setf whitespace-line-column nil whitespace-style '( face trailing tabs tab-mark indentation space-after-tab space-before-tab)) (defun +whitespace-mode-for-writable-buffers () "Turn on `whitespace-mode' if the buffer is writable, off otherwise." (whitespace-mode (if buffer-read-only -1 t))) (add-hook* '(text-mode-hook prog-mode-hook read-only-mode-hook) #'+whitespace-mode-for-writable-buffers) (add-hook 'before-save-hook #'whitespace-cleanup) (define-advice whitespace-cleanup (:around (fn &rest r) preserve-point) (let ((col (current-column))) (apply fn r) (move-to-column col t) (set-buffer-modified-p nil)))) (yoke elisp-mode nil (setf eval-expression-print-length nil eval-expression-print-level nil) (define-key* '(emacs-lisp-mode-map lisp-interaction-mode-map) "C-c C-c" #'eval-defun "C-c C-k" (defun +elisp-eval-region-or-buffer () (interactive) (cond ((region-active-p) (eval-region (region-beginning) (region-end)) (message "Region evaluated.")) (t (eval-buffer) (message "Buffer %s evaluated." (buffer-name))))) "C-c C-z" #'ielm) (define-advice eval-region (:around (fn beg end &rest args) pulse) (apply fn beg end args) (pulse-momentary-highlight-region beg end))) (yoke isearch nil (define-key* (current-global-map) "C-s" #'isearch-forward-regexp "C-r" #'isearch-backward-regexp "C-M-s" #'isearch-forward "C-M-r" #'isearch-backward)) (yoke ispell nil (eval-after ispell (require '+ispell) (add-hook 'before-save-hook #'+ispell-move-buffer-words-to-dir-locals-hook)) (setf ispell-program-name (or (executable-find "ispell") (executable-find "aspell"))) (put 'ispell-buffer-session-localwords 'safe-local-variable #'+ispell-safe-local-p)) (yoke mouse nil ;; Brand new for Emacs 28: see https://ruzkuku.com/texts/emacs-mouse.html ;; Actually, look at this as well: https://www.emacswiki.org/emacs/Mouse3 (when (fboundp 'context-menu-mode) (setf context-menu-functions '(context-menu-ffap context-menu-region context-menu-undo ;; context-menu-dictionary )) (context-menu-mode +1)) (dolist (click '(;; Fix scrolling in the margin wheel-down double-wheel-down triple-wheel-down wheel-up double-wheel-up triple-wheel-up)) (global-set-key (vector 'right-margin click) 'mwheel-scroll) (global-set-key (vector 'left-margin click) 'mwheel-scroll))) (yoke dired nil (require 'dired-x) (setf dired-recursive-copies 'always dired-recursive-deletes 'always dired-create-destination-dirs 'always dired-do-revert-buffer t dired-hide-details-hide-symlink-targets nil dired-isearch-filenames 'dwim delete-by-moving-to-trash t dired-auto-revert-buffer t dired-listing-switches "-AlF" ls-lisp-dirs-first t dired-ls-F-marks-symlinks t dired-clean-confirm-killing-deleted-buffers nil dired-no-confirm '(byte-compile load chgrp chmod chown copy move hardlink symlink shell touch) dired-dwim-target t) (setq-local-hook dired-mode-hook truncate-lines t) (define-key* (current-global-map) "C-x C-j" #'dired-jump) (eval-after dired (define-key* dired-mode-map "" #'dired-up-directory "C-j" #'dired-up-directory)) (add-hook* 'dired-mode-hook #'dired-hide-details-mode #'hl-line-mode)) (yoke dired-hacks "https://github.com/Fuco1/dired-hacks" (define-key* dired-mode-map "TAB" #'dired-subtree-sycle "i" #'dired-subtree-toggle) (add-hook* 'dired-mode-hook #'dired-collapse-mode)) (yoke auth-source nil (setf auth-sources `(default "secrets:passwords")) (setq-local-hook authinfo-mode-hook truncate-lines t)) (yoke consult "https://github.com/minad/consult" (require 'consult) (setf register-preview-delay 0 register-preview-function #'consult-register-format xref-show-xrefs-function #'consult-xref tab-always-indent 'complete completion-in-region-function #'consult-completion-in-region consult-narrow-key "<" consult--regexp-compiler #'consult--default-regexp-compiler) (advice-add #'register-preview :override #'consult-register-window) (define-key* (current-global-map) ;; C-c bindings (mode-specific-map) "C-c h" #'consult-history "C-c m" #'consult-mode-command "C-c b" #'consult-bookmark "C-c k" #'consult-kmacro ;; C-x bindings (ctl-x-map) "C-x M-:" #'consult-complex-command "C-x b" #'consult-buffer "C-x 4 b" #'consult-buffer-other-window "C-x 5 b" #'consult-buffer-other-frame ;; Custom M-# bindings for fast register access "M-#" #'consult-register-load "M-'" #'consult-register-store "C-M-#" #'consult-register ;; Other custom bindings "M-y" #'consult-yank-pop ;;(" a" . consult-apropos) ;; M-g bindings (goto-map) "M-g e" #'consult-compile-error "M-g f" #'consult-flymake ; or consult-flycheck "M-g g" #'consult-goto-line "M-g M-g" #'consult-goto-line "M-g o" #'consult-outline ; or consult-org-heading "M-g m" #'consult-mark "M-g k" #'consult-global-mark "M-g i" #'consult-imenu "M-g M-i" #'consult-imenu "M-g I" #'consult-imenu-multi ;; M-s bindings (search-map) "M-s f" #'consult-find "M-s F" #'consult-locate "M-s g" #'consult-grep "M-s G" #'consult-git-grep "M-s r" #'consult-ripgrep "M-s l" #'consult-line "M-s L" #'consult-line-multi "M-s m" #'consult-multi-occur "M-s k" #'consult-keep-lines "M-s u" #'consult-focus-lines ;; Isearch integration "M-s e" #'consult-isearch-history) (eval-after isearch-mode (define-key* isearch-mode-map "M-e" #'consult-isearch-history "M-s e" #'consult-isearch-history "M-s l" #'consult-line "M-s L" #'consult-line-multi)) (eval-after org (define-key org-mode-map (kbd "M-g o") #'consult-org-heading)) (setf (alist-get ?y (plist-get (alist-get 'emacs-lisp-mode consult-imenu-config) :types)) '("Yoke"))) (yoke orderless "https://github.com/oantolin/orderless" (require 'orderless) (setf completion-styles '(substring orderless basic) completion-category-defaults nil completion-category-overrides '((file (styles basic partial-completion))) orderless-component-separator #'orderless-escapable-split-on-space)) (yoke vertico "https://github.com/minad/vertico" (require 'vertico) (setf resize-mini-windows 'grow-only vertico-count-format nil vertico-cycle t) (vertico-mode)) (yoke embark "https://github.com/oantolin/embark" (require 'embark) (setf prefix-help-command #'embark-prefix-help-command embar-keymap-prompter-key ";") (define-key* (list (current-global-map) 'minibuffer-local-map) "C-." #'embark-act "M-." #'embark-dwim " B" #'embark-bindings) (define-key* embark-file-map "l" #'vlf) (eval-after (embark consult) (require 'embark-consult) (add-hook 'embark-collect-mode-hook #'consult-preview-at-point-mode))) (yoke marginalia "https://github.com/minad/marginalia/" (marginalia-mode)) (yoke wgrep "https://github.com/mhayashi1120/Emacs-wgrep" (require 'wgrep)) (yoke slime "https://github.com/slime/slime" ;; r7rs-swank (let ((r7rsloc (yoke-git "https://github.com/ecraven/r7rs-swank"))) (cond ((executable-find "chibi-scheme") (defun chibi-scheme-start-swank (file encoding) (format "%S\n\n" `(start-swank ,file))) (setq slime-lisp-implementations (cons `(chibi-scheme ("chibi-scheme" ,(format "-A%s" r7rsloc) "-m" "(chibi-swank)") :init chibi-scheme-start-swank) (bound-and-true-p slime-lisp-implementations))))))) (yoke puni "https://github.com/amaikinono/puni" (define-key* puni-mode-map "C-)" #'puni-slurp-forward "C-(" #'puni-slurp-backward "C-}" #'puni-barf-forward "C-{" #'puni-barf-backward "M-(" (defun +puni-open-then-slurp-forward (&optional n) (interactive "p") (insert "()") (backward-char) (puni-slurp-forward n))) (electric-pair-mode) (add-hook* '(prog-mode-hook ielm-mode-hook) #'puni-mode)) (yoke hungry-delete "https://github.com/nflath/hungry-delete" (setq hungry-delete-chars-to-skip " \t" hungry-delete-join-reluctantly nil) (eval-after hungry-delete (add-to-list* 'hungry-delete-except-modes #'eshell-mode #'nim-mode #'python-mode)) (defun +hungry-delete-or (hd-fn fn arg) (funcall (if (looking-back (format "[%s]" hungry-delete-chars-to-skip) arg) hd-fn fn) arg)) (define-key* puni-mode-map [remap puni-backward-delete-char] (defun puni@hungry-delete-backward (arg) (interactive "p") (+hungry-delete-or #'hungry-delete-backward #'puni-backward-delete-char arg)) [remap puni-forward-delete-char] (defun puni@hungry-delete-forward (arg) (interactive "p") (+hungry-delete-or #'hungry-delete-forward #'puni-forward-delete-char arg))) (global-hungry-delete-mode)) (yoke cape "https://github.com/minad/cape" ;; Insinuate in a lot of modes (defvar +capes '(cape-file cape-dabbrev)) (defun +cape-insinuate (hook capf &optional capes) "Insinuate CAPES into a HOOK along with CAPF function. CAPES defaults to `+capes'. CAPF will be made un-exclusive." (setq-local-hook hook completion-at-point-functions (apply #'list (cape-capf-properties capf :exclusive 'no) (or capes +capes)))) (+cape-insinuate 'emacs-lisp-mode-hook #'elisp-completion-at-point)) (yoke minions "https://github.com/tarsius/minions" (minions-mode)) (yoke magit "https://github.com/magit/magit" :load (locate-user-emacs-file "yoke/magit/lisp") :depends ((transient "https://github.com/magit/transient" (locate-user-emacs-file "yoke/transient/lisp")) (dash "https://github.com/magnars/dash.el") (with-editor "https://github.com/magit/with-editor" (locate-user-emacs-file "yoke/with-editor/lisp"))) (autoload #'transient--with-suspended-override "transient") (autoload #'magit "magit" nil :interactive)) (yoke git-modes "https://github.com/magit/git-modes" (require 'git-modes)) (yoke visual-fill-column "https://codeberg.org/joostkremers/visual-fill-column" (setq visual-fill-column-center-text t) (add-hook* 'visual-fill-column-mode-hook #'visual-line-mode) (advice-add 'text-scale-adjust :after #'visual-fill-column-adjust)) (yoke org "https://git.savannah.gnu.org/git/emacs/org-mode.git" :load (locate-user-emacs-file "yoke/org/lisp/") :depends ((org-contrib "https://git.sr.ht/~bzg/org-contrib" (locate-user-emacs-file "yoke/org-contrib/lisp"))) ;; DON'T load system org (setq load-path (cl-remove-if (lambda (path) (string-match-p "lisp/org\\'" path)) load-path)) (setq org-adapt-indentation nil org-auto-align-tags t org-archive-mark-done t org-fold-catch-invisible-edits 'show-and-error org-clock-clocked-in-display 'mode-line org-clock-frame-title-format (cons '(t org-mode-line-string) (cons " --- " frame-title-format)) org-clock-string-limit 7 ; just the clock bit ;; org-clock-string-limit 25 ; gives enough information org-clock-persist nil org-confirm-babel-evaluate nil org-cycle-separator-lines 0 org-directory (sync/ "org/" t) org-ellipsis (or truncate-string-ellipsis "…") org-fontify-done-headline t org-fontify-quote-and-verse-blocks t org-fontify-whole-heading-line t org-hide-emphasis-markers t org-html-coding-system 'utf-8-unix org-image-actual-width (list (* (window-font-width) (- fill-column 8))) org-imenu-depth 3 org-indent-indentation-per-level 0 org-indent-mode-turns-on-hiding-stars nil org-insert-heading-respect-content t org-list-demote-modify-bullet '(("-" . "+") ("+" . "-")) org-log-done 'time org-log-into-drawer t org-num-skip-commented t org-num-skip-unnumbered t org-num-skip-footnotes t org-outline-path-complete-in-steps nil org-pretty-entities t org-pretty-entities-include-sub-superscripts nil org-refile-targets '((nil . (:maxlevel . 2)) (org-agenda-files . (:maxlevel . 1))) org-refile-use-outline-path 'file org-special-ctrl-a/e t org-special-ctrl-k t org-src-fontify-natively t org-src-tab-acts-natively t org-src-window-setup 'current-window org-startup-truncated nil org-startup-with-inline-images t org-tags-column -77 ;; (- (- fill-column 1 (length org-ellipsis))) org-todo-keywords '((sequence "TODO(t)" "WAIT(w@/!)" "ONGOING(o@)" "|" "DONE(d!)" "ASSIGNED(a@/!)") (sequence "|" "CANCELED(k@)") (sequence "MEETING(m)")) org-use-speed-commands t org-emphasis-alist '(("*" org-bold) ("/" org-italic) ("_" org-underline) ("=" org-verbatim) ("~" org-code) ("+" org-strikethrough))) (add-hook* 'org-mode-hook #'variable-pitch-mode #'visual-fill-column-mode #'turn-off-auto-fill #'org-indent-mode #'prettify-symbols-mode #'abbrev-mode) (define-local-before-save-hook org-mode (org-hide-drawer-all) (org-align-tags 'all)) (eval-after org (require '+org) (define-key* org-mode-map "C-M-k" #'kill-paragraph "C-M-t" #'transpose-paragraphs "RET" #'+org-return-dwim "S-" #'+org-table-copy-down|+org-return "C-c C-o" #'+org-open-at-point-dwim) (org-clock-persistence-insinuate))) (yoke org-agenda nil (setq org-agenda-skip-deadline-if-done t org-agenda-skip-scheduled-if-done t org-agenda-span 10 org-agenda-block-separator ?─ org-agenda-time-grid '((daily today require-timed) (800 1000 1200 1400 1600 1800 2000) " ┄┄┄┄┄ " "┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄") org-agenda-current-time-string "← now ─────────────────────────────────────────────────" org-agenda-include-diary nil ; I use the org-diary features org-agenda-todo-ignore-deadlines 'near org-agenda-todo-ignore-scheduled 'future org-agenda-include-deadlines t org-deadline-warning-days 0 org-agenda-show-future-repeats 'next org-agenda-window-setup 'current-window) (setq-local-hook org-agenda-mode-hook truncate-lines t electric-pair-pairs (append electric-pair-pairs (mapcar (lambda (e) (let ((ch (string-to-char (car e)))) (cons ch ch))) org-emphasis-alist))) (add-hook* 'org-agenda-mode-hook #'hl-line-mode) (add-hook 'org-agenda-after-show-hook #'org-narrow-to-subtree) (define-key* (current-global-map) "C-c c" #'org-capture "C-c a" #'org-agenda)) (yoke ox nil ; org-export (eval-after org (require 'ox)) (eval-after ox (require* '+ox '(ox-md nil t)) (+org-export-pre-hooks-insinuate)) (setq org-export-coding-system 'utf-8-unix org-export-headline-levels 8 org-export-with-drawers nil org-export-with-section-numbers nil org-export-with-smart-quotes t org-export-with-sub-superscripts t org-export-with-toc nil)) (yoke electric-cursor "https://codeberg.org/acdw/electric-cursor.el" (setq electric-cursor-alist '((overwrite-mode . hbar) (t . bar))) (electric-cursor-mode)) (yoke _work (sync/ "emacs/private") :depends ((+org-capture (locate-user-emacs-file "lisp")) (private (locate-user-emacs-file "lisp")) (bbdb "https://git.savannah.nongnu.org/git/bbdb.git" (locate-user-emacs-file "yoke/bbdb/lisp")) (bbdb-vcard "https://github.com/tohojo/bbdb-vcard/")) (require 'bbdb) (require* 'private 'work) (bbdb-initialize 'gnus 'message) (setq bbdb-complete-mail-allow-cycling t)) (yoke org-taskwise "https://codeberg.org/acdw/org-taskwise.el") (yoke titlecase "https://codeberg.org/acdw/titlecase.el" (eval-after org (require* 'titlecase '+titlecase)) (eval-after titlecase (add-to-list* 'titlecase-skip-words-regexps (rx word-boundary (+ (any upper digit)) word-boundary)))) (yoke flyspell-correct "https://github.com/duckwork/flyspell-correct" (eval-after flyspell (require* 'flyspell-correct `(+flyspell-correct ,(locate-user-emacs-file "lisp/+flyspell-correct"))) (define-key* flyspell-mode-map "C-;" #'flyspell-correct-wrapper "" #'+flyspell-correct-buffer)) (add-hook 'org-mode-hook #'flyspell-mode) (setq flyspell-correct--cr-key ";")) (yoke helpful "https://github.com/Wilfred/helpful" :depends ((dash "https://github.com/magnars/dash.el") (f "https://github.com/rejeep/f.el") (s "https://github.com/magnars/s.el") (elisp-refs "https://github.com/Wilfred/elisp-refs")) (define-key* (current-global-map) " f" #'helpful-callable " v" #'helpful-variable " k" #'helpful-key " ." #'helpful-at-point " o" #'helpful-symbol) (unless (featurep 'info-look) (run-with-idle-timer 1 nil (lambda () (require 'info-look) (let ((inhibit-message t)) (info-lookup-setup-mode 'symbol 'emacs-lisp-mode))))) (setf (alist-get "\\*helpful" display-buffer-alist nil nil #'string=) '((display-buffer-in-side-window) (side . bottom) (window-height . 20)))) (yoke hippie-completing-read "https://codeberg.org/acdw/hippie-completing-read.el" (define-key* (current-global-map) "M-/" #'hippie-completing-read)) (yoke dictionary nil ; Comes with Emacs 29! (setq dictionary-server "localhost") ; Needs local dictd (setf (alist-get "^\\*Dictionary\\*" display-buffer-alist nil nil #'string=) '((display-buffer-in-side-window) (side . bottom) (window-height . 20)))) (yoke anzu "https://github.com/emacsorphanage/anzu" (global-anzu-mode) (define-key* (current-global-map) [remap query-replace] #'anzu-query-replace-regexp [remap query-replace-regexp] #'anzu-query-replace) (define-key* isearch-mode-map [remap isearch-query-replace] #'anzu-isearch-query-replace-regexp [remap isearch-query-replace-regexp] #'anzu-isearch-query-replace) (defun anzu-qr@window (fn &rest r) "ADVICE to query-replace from the beginning of the window." (let ((scroll-margin 0)) (save-excursion (goto-char (window-start)) (apply fn r)))) (advice-add 'anzu-query-replace-regexp :around #'anzu-qr@window) (advice-add 'anzu-query-replace :around #'anzu-qr@window)) (yoke tempo nil (require '+tempo)) ;; (yoke tempel "https://github.com/minad/tempel" ;; ;; I would use `tempo' but it's clunkier .. :( ;; (define-key* (current-global-map) ;; "M-+" #'tempel-complete ;; "M-_" #'tempel-insert) ;; (defun tempel-capf-insinuate () ;; (setq-local completion-at-point-functions ;; (cons #'tempel-expand ;; completion-at-point-functions))) ;; (add-hook* '(prog-mode-hook ;; text-mode-hook) ;; #'tempel-capf-insinuate)) (yoke scule (locate-user-emacs-file "lisp") (defvar scule-map (let ((map (make-sparse-keymap))) (define-key map (kbd "M-u") #'scule-upcase) (define-key map (kbd "M-l") #'scule-downcase) (define-key map (kbd "M-c") #'scule-capitalize) map) "Keymap for scule twiddling.") (define-key* (current-global-map) "M-c" scule-map "M-u" #'universal-argument) (define-key universal-argument-map (kbd "M-u") #'universal-argument-more)) (yoke 0x0 "https://gitlab.com/willvaughn/emacs-0x0" (setf 0x0-default-server 'ttm) (eval-after embark (define-key* embark-region-map "U" #'0x0-dwim))) (yoke filldent "https://codeberg.org/acdw/filldent.el" (define-key* (current-global-map) "M-q" #'filldent-unfill-toggle)) (yoke avy "https://github.com/abo-abo/avy" (require 'avy) (setf avy-background t (alist-get ?. avy-dispatch-alist) (defun avy-action-embark (pt) (unwind-protect (save-excursion (goto-char pt) (embark-act)) (select-window (cdr (ring-ref avy-ring 0)))) t)) (define-key* (current-global-map) "M-j" #'avy-goto-char-timer) (define-key* isearch-mode-map "M-j" #'avy-isearch)) (yoke frowny "https://codeberg.org/acdw/frowny.el" (setf frowny-eyes (rx (any ":=") (opt "'") (? "-"))) (global-frowny-mode)) (yoke isearch-mb "https://github.com/astoff/isearch-mb" (eval-after (consult anzu) (require 'isearch-mb) (dolist (spec '((isearch-mb--with-buffer ("M-e" . consult-isearch) ("C-o" . loccur-isearch)) (isearch-mb--after-exit ("M-%" . anzu-isearch-query-replace) ("M-s l" . consult-line)))) (let ((isearch-mb-list (car spec)) (isearch-mb-binds (cdr spec))) (dolist (cell isearch-mb-binds) (let ((key (car cell)) (command (cdr cell))) (when (fboundp command) (add-to-list isearch-mb-list command) (define-key isearch-mb-minibuffer-map (kbd key) command))))))) (isearch-mb-mode)) (yoke keepassxc-shim "https://codeberg.org/acdw/keepassxc-shim.el" (keepassxc-shim-activate)) (yoke keychain-environment "https://github.com/tarsius/keychain-environment" :when (executable-find "keychain") (keychain-refresh-environment)) (yoke macrostep "https://github.com/joddie/macrostep" (eval-after elisp-mode (require 'macrostep)) (define-key* '(emacs-lisp-mode-map lisp-interaction-mode-map) "C-c e" #'macrostep-expand))