资源说明:Personal emacs configuration
#+STARTUP: showeverything #+TITLE: grzm's Emacs configuration #+AUTHOR: Michael Glaesemann #+PROPERTY: header-args :tangle yes #+BABEL :cache yes This is my dot-emacs. There are many like it, but this one is mine. My dot-emacs is my best friend. It is my life. I must master it as I master my life. My dot-emacs, without me, is useless. Without my dot-emacs, I am useless. * Requirements The configuration requires =use-package=. I generally use this configuration with [[https://emacsformacosx.com][Emacs for Mac OS X]]. * Initialization My brain works better in lexical scope. =lexical-binding= is the default for =(version< emacs-version "27.1")=. That's what I'm using now, so I've commented this out. I'm leaving it for reference. #+begin_src emacs-lisp ;;; -*- lexical-binding: t -*- #+end_src ** Debugging (when I want it) #+begin_src emacs-lisp ;; (setq debug-on-error t) #+end_src And if I'm debugging, I'll likely want to start Emacs with ~--debug-init -nw~. With Emacs for Mac OS X, that's #+begin_src shell :tangle no ~/Applications/Emacs.app/Contents/MacOS/Emacs --debug-init -nw #+end_src Including this shell block results in some additional message during startup: #+begin_quote Setting up indent for shell type zsh Indentation variables are now local. Indentation setup for shell type zsh #+end_quote These messages are hard-coded when =sh-mode= is invoked. You can get rid of these messages using advice [[https://emacs.stackexchange.com/a/53009][this response]] in Stack Overflow. Another way would be to use [[https://github.com/raxod502/el-patch][el-patch]] to remove these hard-coded messages as Radon Rosborough does in the [[https://github.com/raxod502/radian/commit/4ae79d629c4360a5f281cfc330154c900720d4b1][radian package]]. But for now I'll just leave this note. (Wow. [[https://github.com/raxod502][Radon Rosborough]] is cranking out great Emacs packages!) ** Initialization timing I'm interested in how long Emacs initialization takes. This isn't something I monitor closely, but I can't measure it if I don't add code to do so. This is lifted from [[https://github.com/jwiegley/dot-emacs][John Wiegley's config]], the gentleman who brought us (among other things), =use-package=. Not surprisingly, he's /very/ interested in initialization performance. His is another Emacs config that's worth reviewing for ideas. #+begin_src emacs-lisp (defconst emacs-start-time (current-time)) (unless noninteractive (message "Loading %s..." load-file-name)) #+end_src ** Tangling =init.el= is loaded first, which, among other things, tangles and loads this file, which contains the majority of my Emacs configuration. The tangled output actually replaces the =init.el= in the repo. After initial checkout, I don't want to track this generated replacement, so I can tell git to ignore it: #+begin_src shell :tangle no git update-index --assume-unchanged init.el #+end_src And if there /are/ modifications I want to make, I can tell git to track changes again. #+begin_src shell :tangle no git update-index --no-assume-unchanged init.el #+end_src If for some reason you want to start from the committed =init.el= again, just check it out. Git will pull it from the index, but still not track it. #+begin_src shell :tangle no git checkout init.el #+end_src And let's have this file automatically re-tangled on save to keep =init.el= up-to-date. #+begin_src emacs-lisp (defun tangle-init () "If the current buffer is 'init.org' the code-blocks are tangled, and the tangled file is compiled." (when (equal (buffer-file-name) (expand-file-name (concat user-emacs-directory "init.org"))) ;; Avoid running hooks when tangling. (let ((prog-mode-hook nil)) (org-babel-tangle)))) (add-hook 'after-save-hook 'tangle-init) #+end_src This method of is largely based on [[https://github.com/larstvei/dot-emacs][Lars Tveito's config]]. He also byte-compiles the output. I haven't seen any performance advantage of byte-compiling my setup, so I've removed that as it brings along another layer of complexity. There's a lot of other great stuff his config; it's well worth a look. ** straight.el I want an immutable package manager. I'm primarily waiting on three things: * determining how I can easily detect if my packages are out of date and update them individually * convince myself there aren't gotchas that will be too much work to work around * convince myself that the project is going to be maintained for the foreseeable future. I was convinced that I could move to [[https://github.com/raxod502/straight.el][straight.el]] by a blog post by David Crook, [[https://github.crookster.org/switching-to-straight.el-from-emacs-26-builtin-package.el/]["Switching to straight.el from Emacs 26 builtin package.el"]]. The situation has improved quite a bit since the post was written (13 November 2018); the org-specific fixes are now part of straight itself. I tried this first on 2020-12-06, but reverted because I couldn't figure out issues around org-ref (likely related to my ignorance around autoloading). However, the melpa issues on [[https://github.com/melpa/melpa/issues/7312#issuecomment-751142747][melpa packaging issues on 2020-12-24]], um, scared me straight, so I'm giving it another go. First, bootstrap straight.el. #+begin_src emacs-lisp (defvar bootstrap-version) (let ((bootstrap-file (expand-file-name "straight/repos/straight.el/bootstrap.el" user-emacs-directory)) (bootstrap-version 5)) (unless (file-exists-p bootstrap-file) (with-current-buffer (url-retrieve-synchronously "https://raw.githubusercontent.com/raxod502/straight.el/develop/install.el" 'silent 'inhibit-cookies) (goto-char (point-max)) (eval-print-last-sexp))) (load bootstrap-file nil 'nomessage)) (straight-use-package 'use-package) (setq straight-use-package-by-default t) (setq-default use-package-always-defer t) #+end_src ** Detection #+begin_src emacs-lisp (defun is-mac-p () (eq system-type 'darwin)) (defun is-linux-p () (eq system-type 'gnu/linux)) (defun grzm/is-koke-p () (string-equal (system-name) "koke.local")) (defun grzm/is-m1-arm-p () (grzm/is-koke-p)) (defun grzm/use-selectrum-p () (grzm/is-koke-p)) (defun grzm/use-prescient-p () (grzm/use-selectrum-p)) (defun grzm/prefer-helm-p () (not (grzm/use-selectrum-p))) (defun grzm/use-ivy-p () (not (grzm/use-selectrum-p))) #+end_src The built-in ~window-system~ function and variable are also useful (and used below) when modifying configuration for particular machines and environments. For example, I don't do work with org-roam or pdfs when not using gui Emacs, so I'm including them only ~:if window-system~. * Configuration Set default directory to =$HOME= when it's not otherwise set (such as startup). It can default to =/= which is rarely what I want. #+begin_src emacs-lisp (setq default-directory "~/") #+end_src All of my configuration should be declared here. I'm not going to use the custom settings interface, so use a temporary file for anyone that thinks they need to reference it. #+begin_src emacs-lisp (setq custom-file (make-temp-file "emacs--custom-file")) #+end_src Besides being Lisp, =cl= provides =labels= and =defvar=, used below. Wrapped gently in =eval-when-compile= to silence #+begin_quote Warning (bytecomp): cl package required at runtime #+end_quote #+begin_src emacs-lisp (eval-when-compile (let* ((grzm/cl-package-deprecated-p (<= 27 emacs-major-version))) (when (not grzm/cl-package-deprecated-p) (require 'cl)))) #+end_src #+begin_src emacs-lisp (prefer-coding-system 'utf-8) #+end_src #+begin_src emacs-lisp (eval-and-compile (mapc #'(lambda (path) (add-to-list 'load-path (expand-file-name path user-emacs-directory))) '("elisp" "elpa"))) #+end_src #+begin_src emacs-lisp (let* ((local-org-mode-lisp "~/dev/org-mode/lisp")) (when (file-exists-p local-org-mode-lisp) (add-to-list 'load-path (expand-file-name "~/dev/org-mode/lisp")))) #+end_src #+begin_src emacs-lisp (defsubst hook-into-modes (func &rest modes) (dolist (mode-hook modes) (add-hook mode-hook func))) #+end_src Use =y= and =n= in lieu of =yes= and =no= in mini-buffer prompts. #+begin_src emacs-lisp (fset 'yes-or-no-p 'y-or-n-p) #+end_src #+begin_src emacs-lisp (setq confirm-nonexistent-file-or-buffer nil) #+end_src #+begin_src emacs-lisp (setq kill-buffer-query-functions (remq 'process-kill-buffer-query-function kill-buffer-query-functions)) #+end_src #+begin_src emacs-lisp (defconst sans-font-family "Source Code Pro") (defconst variable-font-family "OfficinaSansITCStd") (set-face-attribute 'default nil :family sans-font-family :weight 'normal :height 140) (set-face-attribute 'fixed-pitch nil :family sans-font-family :height 1.0) (set-face-attribute 'variable-pitch nil :family variable-font-family :height 1.2) (setq-default line-spacing 0.4) #+end_src #+begin_src emacs-lisp (defun clean-up-buffer () "Indent the entire buffer." (interactive) (delete-trailing-whitespace) (indent-region (point-min) (point-max) nil) (untabify (point-min) (point-max))) #+end_src #+begin_src emacs-lisp (let ((sources (if (is-mac-p) (quote ("~/.authinfo" "~/.authinfo.gpg" "~/.netrc" macos-keychain-internet)) (quote ("~/.authinfo" "~/.authinfo.gpg" "~/.netrc"))))) (setq auth-sources sources)) #+end_src I don't want to hear about advice redefinitions, messages like #+begin_quote ad-handle-definition: ‘vc-revert’ got redefined #+end_quote From [[https://andrewjamesjohnson.com/suppressing-ad-handle-definition-warnings-in-emacs/][Andrew Johnson]] #+begin_src emacs-lisp (setq ad-redefinition-action 'accept) #+end_src ** UI settings Quiet startup: Don't display the splash screen, start with a clear scratch buffer, and don't display /"For information about GNU Emacs and the GNU system, type C-h C-a."/. This last is surprisingly difficult to do, and requires both the =put= call and setting the value to your user name. From https://emacs.stackexchange.com/a/36303, with one change: use a non-empty list (e.g., =(t)=) instead of =t= for the ='saved-value= or you'll see errors like /(wrong-type-argument listp t)/ when Emacs first tries to write customizations, and errors like /'forward-sexp: Scan error: "Unbalanced parentheses"/ when it subsequently tries to read the malformed custom-file contents it attempted to write. #+begin_src emacs-lisp (put 'inhibit-startup-echo-area-message 'saved-value '(t)) (setq inhibit-startup-screen t initial-scratch-message "" inhibit-startup-echo-area-message user-login-name) #+end_src When in a windowing system, set the default window size. #+begin_src emacs-lisp (when (window-system) (setq initial-frame-alist `((top . 0) (left . 0.5) (height . 1.0) (width . 100))) (setq default-frame-alist (copy-alist initial-frame-alist))) #+end_src #+begin_src emacs-lisp (add-to-list 'custom-theme-load-path (file-name-as-directory (expand-file-name "themes" user-emacs-directory))) (let* ((theme (cond ((window-system) 'grzm-ivory) ((is-linux-p) 'solarized-dark) (t 'minima-ebony)))) (load-theme theme t)) #+end_src Hide the toolbar. #+begin_src emacs-lisp (if (fboundp 'tool-bar-mode) (tool-bar-mode -1)) (if (fboundp 'menu-bar-mode) (menu-bar-mode -1)) ;; I generally want scrollbars, but as a reminder of the option: ;; (if (fboundp 'scroll-bar-mode) (scroll-bar-mode -1)) #+end_src Always display the column number. #+begin_src emacs-lisp (setq column-number-mode t) #+end_src Who likes to be interrupted by a bell? I certainly don't. #+begin_src emacs-lisp (setq ring-bell-function 'ignore) #+end_src ** Editing #+begin_src emacs-lisp (setq-default indent-tabs-mode nil) ;; insert multiple spaces instead of tabs #+end_src #+begin_src emacs-lisp (show-paren-mode t) #+end_src #+begin_src emacs-lisp ;; Use C-x C-m as a shortcut for M-x: Let's save our small fingers! ;; Hint from Steve Yegge: http://steve.yegge.googlepages.com/effective-emacs ;; Invoice M-x without the alt key (global-set-key "\C-x\C-m" 'execute-extended-command) ;; Steve also recommends adding C-c C-m to allow for slop in hitting C-x ;; Don't know if I'll need that now, but it might be nice in the future ;;(global-set-key "\C-x\C-m" 'execute-extended-command) ;; However, I often mistype C-x m and I don't use mail, so (global-set-key "\C-xm" 'execute-extended-command) ;;; Unbind `C-x f', which, by default sets fill-text width, which is uncommon (global-unset-key "\C-xf") ;;; Rebind `C-x C-b' for 'buffer-menu', rather than list-buffers (global-set-key "\C-x\C-b" 'buffer-menu) ;; Item 3: Prefer backward-kill-word over Backspace ;; Another of Steve Yegge's hints ;; For fast typists, it's faster to retype a word rather than backspace ;; to fix just the error, so map this to C-w. However, C-w is already ;; mapped for kill-region, so remap kill-region to C-x C-k (global-set-key "\C-w" 'backward-kill-word) (global-set-key "\C-x\C-k" 'kill-region) ;; Again, Steve maps C-c C-k as well ;; (global-set-key "\C-c\C-k" 'kill-region) ;; Item 9: Master Emacs's regular expressions ;; Bind M-r and M-s to isearch-forward-regexp and isearch-backward-regexp ;; Note that this stomps on the default binding for move-to-window-line (M-r) (global-set-key "\M-s" 'isearch-forward-regexp) (global-set-key "\M-r" 'search-backward-regexp) ;; Since query-replace-regexp and (replace-regexp) are so useful, ;; give them abbreviated aliases (defalias 'rr 'replace-regexp) (defalias 'qrr 'query-replace-regexp) ;; from http://wiki.rubygarden.org/Ruby/page/show/InstallingEmacsExtensions ;; This is also of interrest, it automagically does a "chmod u+x" when you ;; save a script file (starting with "#!"). (add-hook 'after-save-hook 'executable-make-buffer-file-executable-if-script-p) (setq backup-directory-alist `(("." . "~/.saves"))) (setq backup-by-copying t) (put 'downcase-region 'disabled nil) (put 'upcase-region 'disabled nil) (setq vc-follow-symlinks t) #+end_src ** f and s I use the =f= (file) and =s= (string) packages to configure subsequent packages, so load them first. *** f #+begin_src emacs-lisp (use-package f :commands (f-touch)) #+end_src *** s #+begin_src emacs-lisp (use-package s :commands (s-downcase s-join s-trim)) #+end_src ** Work/Personal separation I have some work-specific Emacs configuration that I like to keep in a separate repo from my personal setup. Let's see if it's there. #+begin_src emacs-lisp (defvar work-emacs-directory "~/.emacs.work.d/") (defvar work-p (file-exists-p work-emacs-directory)) #+end_src #+begin_src emacs-lisp (when work-p (org-babel-load-file (expand-file-name "init.org" work-emacs-directory))) #+end_src If it's not, continue with my usual config. #+begin_src emacs-lisp (unless (or work-p (is-linux-p)) (defvar grzm/org-directory "~/org/") (defvar grzm/beorg-directory "/Users/grzm/Library/Mobile Documents/iCloud~com~appsonthemove~beorg/Documents/org/") (setq grzm/org-inbox-dot-org (expand-file-name "inbox.org" grzm/org-directory) grzm/org-todo-dot-org (expand-file-name "todo.org" grzm/org-directory) grzm/org-someday-maybe-dot-org (expand-file-name "someday-maybe.org" grzm/org-directory) grzm/beorg-inbox-dot-org (expand-file-name "inbox.org" grzm/beorg-directory) grzm/org-template-directory (expand-file-name "org/templates/" user-emacs-directory) grzm/zotero-bib "~/Documents/references/bibliography/zotero.bib" org-capture-templates `(("t" "Task" entry (file ,grzm/org-inbox-dot-org) (file ,(expand-file-name "task.org" grzm/org-template-directory)) :prepend t :empty-lines 1) ("p" "Project" entry (file+headline ,grzm/org-todo-dot-org "Projects") (file ,(expand-file-name "project.org" grzm/org-template-directory)) :empty-lines 1) ("w" "web reference" entry (file ,grzm/org-inbox-dot-org) (file ,(expand-file-name "web-ref.org" grzm/org-template-directory)) :prepend t :empty-lines 1)) org-refile-targets `(((,grzm/org-todo-dot-org ,grzm/org-someday-maybe-dot-org) :maxlevel . 3)) org-tag-alist '(("q" . ?q) ("automower" . ?a) ("postgresql" . ?p) ("emacs" . ?e)) org-agenda-custom-commands '(("n" "Agenda and all TODOs" ((agenda "" nil) (alltodo "" nil)) nil)) org-roam-directory "~/org/org-roam" org-roam-db-location (expand-file-name "org-roam.db" org-roam-directory) grzm/bibtex-notes-directory (expand-file-name "bibliography/notes/" org-roam-directory))) #+end_src I've found Tasshin Michael Fogleman's work on using org-mode with GTD really helpful. The templates and daily-review function are built on his work. See his [[https://github.com/mwfogleman/.emacs.d/blob/master/michael.org#capture-templates][Emacs config]] and his [[https://gist.github.com/mwfogleman/267b6bc7e512826a2c36cb57f0e3d854][Building a Second Brain templates]]. He also has posted a [[https://www.youtube.com/watch?v=LQwjSd3X9xE][video of how he uses them during his daily review]]. #+begin_src emacs-lisp (unless work-p (setq grzm/review-dot-org "/tmp/reviews.org") (f-touch grzm/review-dot-org) ;; This is buggy: first time after startup, calling C-c r, d raises the following error: ;; (error "No capture template referred to by \"d\" keys") ;; Subsequent calls seem fine, as does calling the function directly. (defun grzm/new-daily-review () (interactive) (let ((org-capture-templates `(("d" "Review: Daily Review" entry (file+olp+datetree ,grzm/review-dot-org) (file ,(expand-file-name "daily-review.org" grzm/org-template-directory)))))) (progn (org-capture nil "d") (org-capture-finalize t) (org-speed-move-safe 'outline-up-heading) (org-narrow-to-subtree) (org-clock-in)))) (bind-keys :prefix-map review-map :prefix "C-c r" ("d" . grzm/new-daily-review))) #+end_src ** packages *** autopair #+begin_src emacs-lisp (use-package autopair) #+end_src *** avy #+begin_src emacs-lisp (use-package avy :bind (("C-:" . avy-goto-char) ("C-'" . avy-goto-char-2) ("M-g g" . avy-goto-line))) #+end_src *** beacon #+begin_src emacs-lisp (use-package beacon :if window-system :config (setq beacon-size 80 beacon-color "#bbb" beacon-blink-duration 0.1 beacon-blink-delay 0.1) (beacon-mode 1)) #+end_src *** blackout #+begin_src emacs-lisp (use-package blackout) (with-eval-after-load 'auto-revert (blackout 'auto-revert-mode)) (with-eval-after-load 'eldoc (blackout 'eldoc-mode)) #+end_src *** browse-kill-ring #+begin_src emacs-lisp (use-package browse-kill-ring) #+end_src *** cider #+begin_src emacs-lisp (use-package cider :defines cider-prompt-save-file-on-load :config (setq cider-prompt-save-file-on-load nil cider-eval-result-prefix " ;; => " cider-font-lock-dynamically '(macro core function var) cider-repl-pop-to-buffer-on-connect 'display-only cider-repl-display-help-banner nil cider-repl-use-pretty-printing t cider-boot-parameters "cider repl -w wait")) #+end_src #+begin_src emacs-lisp (put 'cider-clojure-cli-global-options 'safe-local-variable #'stringp) (put 'cider-preferred-build-tool 'safe-local-variable #'symbolp) (put 'cider-boot-parameters 'safe-local-variable #'stringp) #+end_src *** clj-refactor #+begin_src emacs-lisp (use-package clj-refactor :config (setq cljr-assume-language-context (quote clj) cljr-clojure-test-declaration "[clojure.test :as test :refer [are deftest is]]") ;; :bind ("/" . cljr-slash) ) #+end_src *** clojure-mode #+begin_src emacs-lisp (use-package clojure-mode :blackout "λ" :config (setq clojure-indent-style :align-arguments clojure-align-forms-automatically nil) (defun my-clojure-mode-hook () (paredit-mode +1) (put-clojure-indent 'defui '(1 nil nil (1))) (rainbow-delimiters-mode)) (add-to-list 'interpreter-mode-alist '("bb" . clojure-mode)) (add-hook 'clojure-mode-hook 'my-clojure-mode-hook) (add-hook 'clojure-mode-hook 'lsp) (add-hook 'clojurescript-mode-hook 'lsp) (add-hook 'clojurec-mode-hook 'lsp)) #+end_src *** company #+begin_src emacs-lisp (use-package company :blackout " ㍿" :init (setq company-idle-delay nil company-async-timeout 15 company-tooltip-align-annotations t) :hook (after-init . global-company-mode)) #+end_src *** company-prescient #+begin_src emacs-lisp (use-package company-prescient :init (company-prescient-mode +1)) #+end_src *** ctrlf isearch replacement #+begin_src emacs-lisp (use-package ctrlf :config (ctrlf-mode t)) #+end_src *** delight #+begin_src emacs-lisp (use-package delight) #+end_src *** dired =ls= on Darwin doesn't support the =--dired= option. https://stackoverflow.com/a/42038174 #+begin_src emacs-lisp (when (is-mac-p) (setq dired-use-ls-dired nil)) #+end_src *** dockerfile-mode #+begin_src emacs-lisp (use-package dockerfile-mode :mode "Dockerfile[a-zA-Z.-]*\\'") #+end_src *** el-patch #+begin_src emacs-lisp (use-package el-patch) #+end_src *** emacs-lisp-mode Structural editing rocks. Use it for =emacs-lisp=, too. #+begin_src emacs-lisp (add-hook 'emacs-lisp-mode-hook (lambda () (paredit-mode +1))) #+end_src *** exec-path-from-shell #+begin_src emacs-lisp (use-package exec-path-from-shell :demand t :if (memq window-system '(mac ns)) :config (exec-path-from-shell-initialize)) #+end_src *** flycheck #+begin_src emacs-lisp (use-package flycheck :init (global-flycheck-mode)) #+end_src *** git-gutter and git-gutter-fringe #+begin_src emacs-lisp (let ((package-name (if (display-graphic-p) 'git-gutter-fringe 'git-gutter))) (eval `(use-package ,package-name :init (global-git-gutter-mode)))) ;; Don't need git-gutter to tell us it's active. (setq git-gutter:lighter "") #+end_src *** graphviz-dot-mode #+begin_src emacs-lisp (use-package graphviz-dot-mode :config (setq graphviz-dot-ident-width 2)) #+end_src *** helm Binding =M-y= to =helm-show-kill-ring= is from [[https://sachachua.com/blog/2014/12/emacs-m-y-helm-show-kill-ring/][Sacha Chua]]. #+begin_src emacs-lisp (use-package helm :blackout " Ⓗ" :bind (("M-y" . helm-show-kill-ring) :map helm-map ("" . helm-execute-persistent-action) ("C-z" . helm-select-action) ("A-v" . helm-previous-page)) :config (when (grzm/prefer-helm-p) (helm-mode 1) (helm-autoresize-mode 1))) #+end_src *** helm-org-rifle #+begin_src emacs-lisp (use-package helm-org-rifle) #+end_src *** helm-bibtex helm-bibtex includes bibtex-completion, so =bibtex-completion-*= configuration is here. #+begin_src emacs-lisp (use-package helm-bibtex :config (unless (or work-p (is-linux-p)) (setq bibtex-completion-bibliography grzm/zotero-bib bibtex-completion-notes-path grzm/bibtex-notes-directory bibtex-completion-pdf-field "file" bibtex-completion-notes-template-multiple-files (f-read-text (expand-file-name "bibtex-completion-notes-template-multiple-files" grzm/org-template-directory))))) #+end_src *** inf-clojure #+begin_src emacs-lisp (use-package inf-clojure) #+end_src *** ivy #+begin_src emacs-lisp (when (grzm/use-ivy-p) (use-package ivy :config (setq ivy-use-virtual-buffers t enable-recursive-minibuffers t) (ivy-mode 1) :bind ("C-w" . ivy-backward-kill-word))) #+end_src *** lsp-mode #+begin_src emacs-lisp (use-package lsp-mode :delight '("
本源码包内暂不包含可直接显示的源代码文件,请下载源码包。