From 3514f0b40858eed04fe10e63d4fc593b4d1d5085 Mon Sep 17 00:00:00 2001 From: Case Duckworth Date: Thu, 21 Jan 2021 08:39:01 -0600 Subject: Reorganize and add stuff --- config.org | 913 ++++++++++++++++++++++++++++++++++++------------------------- 1 file changed, 540 insertions(+), 373 deletions(-) diff --git a/config.org b/config.org index ac18133..b27fb35 100644 --- a/config.org +++ b/config.org @@ -1,143 +1,27 @@ #+TITLE: Emacs configuration, literate-style #+AUTHOR: Case Duckworth -#+PROPERTY: header-args :tangle yes :tangle-mode (identity #o444) :comments both -* Settings +* About me -Basic settings necessary for a decent editing experience in Emacs. -These should not require non-built-in packages. - -** Prelude - -*** Enable lexical binding - -#+begin_src emacs-lisp :comments no - ;; config.el -*- lexical-binding: t -*- -#+end_src - -*** Disclaimer - -#+NAME: disclaimer -#+begin_src emacs-lisp :comments no - ;; This file is automatically tangled from config.org. - ;; Hand edits will be overwritten! -#+end_src - -** Customization - -*** Emulate use-package's =:custom= - -#+begin_src emacs-lisp - (defmacro cuss (var val &optional _docstring) - "`use-package''s `:custom', without `use-package'." - (declare (doc-string 3) - (indent 2)) - `(funcall (or (get ',var 'custom-set) #'set-default) - ',var ,val)) -#+end_src - -*** Emulate use-package's =:custom-face= - -#+begin_src emacs-lisp - (defvar acdw--custom-faces () - "List of custom faces to run through `acdw/set-custom-faces'.") - - (defun acdw/set-custom-faces () - "Customize faces using `customize-set-faces'. - - I only want to run this once, per the documentation of `customize-set-faces'." - (when acdw--custom-faces - (let ((msg "Customizing faces")) - (message "%s..." msg) - (apply #'custom-set-faces acdw--custom-faces) - (message "%s...Done." msg) - (remove-function after-focuse-change-function #'acdw/set-custom-faces)))) - - (add-function :before after-focus-change-function #'acdw/set-custom-faces) - - (defmacro cussface (face spec &optional _docstring) - "Add the form (FACE SPEC) to `acdw--custom-faces'." - (declare (doc-string 3) - (indent defun)) - `(add-to-list 'acdw--custom-faces '(,face ,spec))) -#+end_src - -*** Only do something when Emacs is unfocused - -Since Emacs is single-threaded, I only want to run really expensive -operations when I won't notice, say .. when I'm focused on another -window. - -#+begin_src emacs-lisp - (defun when-unfocused (func &rest args) - "Run FUNC with ARGS iff all frames are out of focus." - (when (seq-every-p #'null (mapcar #'frame-focus-state (frame-list))) - (apply func args))) -#+end_src - -*** Throw customizations away - -I use Emacs's Customize interface, but really only to learn about what -options a package presents to /be/ customized. I don't want to use -the custom file for anything at all. - -#+begin_src emacs-lisp - (cuss custom-file null-device) -#+end_src - -** About me - -My name and email address. - -#+begin_src emacs-lisp +#+begin_src emacs-lisp :noweb-ref settings (setq user-full-name "Case Duckworth" user-mail-address "acdw@acdw.net") #+end_src -** Look and feel - -*** Cursor - -#+begin_src emacs-lisp - ;; Show a vertical bar cursor - (cuss cursor-type 'bar) - - ;; Hide the cursor in other windows - (cuss cursor-in-non-selected-windows nil) - - ;; Don't blink the cursor - (blink-cursor-mode -1) -#+end_src - -*** Tabs - -**** Tab names should be current buffer + a count of windows - -#+begin_src emacs-lisp - (cuss tab-bar-tab-name-function - #'tab-bar-tab-name-current-with-count) -#+end_src - -**** Only show the tab bar when there's more than one tab - -For some reason, this doesn't work with multiple frames. - -#+begin_src emacs-lisp - (cuss tab-bar-show 1) -#+end_src - -*** Frames +* Look and feel -/Frames/ are Emacs's concepts that generally correspond to other -programs' /windows/ -- that is, they're the boxen on the screen that -contain the Emacs programmen. +** Frames -**** Initial frame setup +*** Initial frame setup :PROPERTIES: :header-args: :noweb-ref initial-frame-setup :END: -***** Tool bar +I tangle this section to =early-init.el=, since that's evaluated +before GUI set-up. Which, in turn, means Emacs will skip the "flash +of unstyled content" thing. + +**** Tool bar #+begin_src emacs-lisp (add-to-list 'default-frame-alist @@ -146,7 +30,7 @@ contain the Emacs programmen. (tool-bar-mode -1) #+end_src -***** Menu bar +**** Menu bar #+begin_src emacs-lisp (add-to-list 'default-frame-alist @@ -155,7 +39,7 @@ contain the Emacs programmen. (menu-bar-mode -1) #+end_src -***** Scroll bars +**** Scroll bars #+begin_src emacs-lisp (add-to-list 'default-frame-alist @@ -166,44 +50,44 @@ contain the Emacs programmen. (horizontal-scroll-bar-mode -1) #+end_src -**** Frame titles - -Set the frame title to something more useful than the default: include -the current buffer and the current filename. +*** Frame titles -#+begin_src emacs-lisp - (cuss frame-title-format - (concat invocation-name "@" (system-name) - ": %b %+%+ %f")) +#+begin_src emacs-lisp :noweb-ref settings + (setq-default frame-title-format + (concat invocation-name "@" (system-name) + ": %b %+%+ %f")) #+end_src -**** Fringes +*** Fringes +:PROPERTIES: +:header-args: :noweb-ref settings +:END: I have grown to love Emacs's little fringes on the side of the windows. In fact, I love them so much that I really went overboard and have made a custom fringe bitmap. -***** Indicate empty lines after the end of the buffer +**** Indicate empty lines after the end of the buffer #+begin_src emacs-lisp - (cuss indicate-empty-lines t) + (setq-default indicate-empty-lines t) #+end_src -***** Indicate the boundaries of the buffer +**** Indicate the boundaries of the buffer #+begin_src emacs-lisp - (cuss indicate-buffer-boundaries 'right) + (setq-default indicate-buffer-boundaries 'right) #+end_src -***** Indicate continuation lines, but only on the left fringe +**** Indicate continuation lines, but only on the left fringe #+begin_src emacs-lisp - (cuss visual-line-fringe-indicators '(left-curly-arrow nil)) + (setq-default visual-line-fringe-indicators '(left-curly-arrow nil)) #+end_src -***** Customize fringe bitmaps +**** Customize fringe bitmaps -****** Curly arrows (continuation lines) +***** Curly arrows (continuation lines) #+begin_src emacs-lisp (define-fringe-bitmap 'left-curly-arrow @@ -219,7 +103,7 @@ and have made a custom fringe bitmap. #b11000000]) #+end_src -****** Arrows (truncation lines) +***** Arrows (truncation lines) #+begin_src emacs-lisp (define-fringe-bitmap 'left-arrow @@ -235,139 +119,162 @@ and have made a custom fringe bitmap. #b00000000]) #+end_src -*** Windows - -**** Winner mode - -I don't really /use/ winner-mode as of yet, but it seems like a really -good thing to have. It lets you move between window configurations -with =C-c <-/->=. - -#+begin_src emacs-lisp - (when (fboundp 'winner-mode) - (winner-mode +1)) -#+end_src +** Windows -**** Switch windows, or buffers if there's only one +*** Switch to other window or buffer -from [[https://www.reddit.com/r/emacs/comments/kz347f/what_parts_of_your_config_do_you_like_best/gjlnp2c/][u/astoff1]]. +I grabbed this from [[https://www.reddit.com/r/emacs/comments/kz347f/what_parts_of_your_config_do_you_like_best/gjlnp2c/][u/astoff1]]: it extends the =other-window= command +to switch to the other buffer if there's only one window in the frame. -#+begin_src emacs-lisp +#+begin_src emacs-lisp :noweb-ref functions (defun other-window-or-buffer () - "Switch to the other window, or previous buffer." + "Switch to other window, or the previous buffer." (interactive) (if (eq (count-windows) 1) (switch-to-buffer nil) (other-window 1))) #+end_src -*** Buffers +And I'll bind it to =M-o=, since that's easier to reach than =C-x o=. -**** Startup buffers +#+begin_src emacs-lisp :noweb-ref bindings + (define-key global-map (kbd "M-o") #'other-window-or-buffer) +#+end_src -I don't want to see Emacs's splash screen, and I want the =*scratch*= -buffer to have a little message. +** Buffers -#+begin_src emacs-lisp - (cuss inhibit-startup-screen t - "Don't show the startup buffer.") +*** Uniquify buffers - (cuss initial-buffer-choice t - "Start with *scratch*.") +The default way Emacs makes buffer names unique is really ugly and, +dare I say it, stupid. Instead, I want them to be uniquified by their +filesystem paths. - (cuss initial-scratch-message - (concat ";; Hello, " (nth 0 (split-string user-full-name)) "!\n" - ";; Happy hacking ...")) +#+begin_src emacs-lisp :noweb-ref requires + (require 'uniquify) #+end_src -**** Immortal =*scratch*= buffer +#+begin_src emacs-lisp :noweb-ref settings + (setq-default uniquify-buffer-name-style 'forward) +#+end_src -I don't want to accidentally kill the =*scratch*= buffer. +*** Startup buffers -#+begin_src emacs-lisp - (defun immortal-scratch () - (if (eq (current-buffer) (get-buffer "*scratch*")) - (progn (bury-buffer) - nil) - t)) +When Emacs starts up, I want a blank slate: the *scratch* buffer. I +also want it to show a cute little message to myself. - (add-hook 'kill-buffer-query-functions #'immortal-scratch) +#+begin_src emacs-lisp :noweb-ref settings + (setq-default inhibit-startup-screen t ; Don't show that splash screen thing. + initial-buffer-choice t ; Start on *scratch* + initial-scratch-message + (concat ";; Howdy, " + (nth 0 (split-string user-full-name)) "!\n" + ";; Welcome to Emacs." + "\n\n")) #+end_src -**** Uniquify buffers +*** Immortal =*scratch*= buffer -I like the =forward= style, which uniquifies buffers by including path -elements up the tree until the names are unique. +I don't want to accidentally kill the *scratch* buffer. So, I add a +function to the =kill-buffer-query-functions= hook that will return +=nil= if the buffer is *scratch*. -#+begin_src emacs-lisp - (require 'uniquify) - (cuss uniquify-buffer-name-style 'forward) +#+begin_src emacs-lisp :noweb-ref functions + (defun immortal-scratch () + (if (not (eq (current-buffer) (get-buffer "*scratch*"))) + t + (bury-buffer) + nil)) #+end_src -**** Kill buffers more smarter-ly - -#+begin_src emacs-lisp - (defun kill-a-buffer (&optional prefix) - "Kill buffers and windows sanely. +#+begin_src emacs-lisp :noweb-ref hooks + (add-hook 'kill-buffer-query-functions #'immortal-scratch) +#+end_src - `kill-a-buffer' works based on the prefix argument as follows: +*** Kill buffers better - - 0 => kill the CURRENT buffer and window - - 4 (C-u) => kill the OTHER window and its buffer - - 16 (C-u C-u) => kill ALL OTHER buffers and windows +#+begin_src emacs-lisp :noweb-ref functions + (defun kill-a-buffer (&optional prefix) + "Kill a buffer and its window, prompting only on unsaved changes. - Prompt iff there are unsaved changes." + `kill-a-buffer' uses the PREFIX argument to determine which buffer(s) to kill: + 0 => Kill current buffer & window + 4 (C-u) => Kill OTHER buffer & window + 16 (C-u C-u) => Kill ALL OTHER buffers & windows" (interactive "P") (pcase (or (car prefix) 0) - (0 (kill-current-buffer) - (unless (one-window-p) (delete-window))) - (4 (other-window 1) - (kill-current-buffer) - (unless (one-window-p) (delete-window))) + (0 (kill-current-buffer) + (unless (one-window-p) (delete-window))) + (4 (other-window 1) + (kill-current-buffer) + (unless (one-window-p) (delete-window))) (16 (mapc #'kill-buffer (delq (current-buffer) (buffer-list))) (delete-other-windows)))) #+end_src -*** Fonts +#+begin_src emacs-lisp :noweb-ref bindings + (define-key ctl-x-map "k" #'kill-a-buffer) +#+end_src -**** Function: =set-face-from-alternatives= +** Cursor -To be honest, this might be better off using =cussface=, but that's -another story for another day. +*** Cursor shape -#+begin_src emacs-lisp - (defun set-face-from-alternatives (face frame &rest fontspecs) - "Set FACE on FRAME from first available font from FONTSPECS. - FACE and FRAME work the same as with `set-face-attribute'." - (catch :return - (dolist (spec fontspecs) - (when-let ((found (find-font (apply #'font-spec spec)))) - (set-face-attribute face frame :font found) - (throw :return found))))) +I like a vertical bar, but only in the selected window. + +#+begin_src emacs-lisp :noweb-ref settings + (setq-default cursor-type 'bar + cursor-in-non-selected-windows nil) #+end_src -**** Add a hook to setup fonts after the first window focus change +*** Don't blink the cursor -Of course, I only need to setup the fonts on a graphical session. +#+begin_src emacs-lisp :noweb-ref modes + (blink-cursor-mode -1) +#+end_src -#+begin_src emacs-lisp - (when (display-graphic-p) - (add-function :before after-focus-change-function #'acdw/setup-fonts)) +** Tabs + +*** Tab names + +#+begin_src emacs-lisp :noweb-ref settings + (setq-default tab-bar-tab-name-function + #'tab-bar-tab-name-current-with-count) #+end_src -**** Setup my fonts +*** When to show the tab bar -Notice that this function removes itself from -=after-focus-change-function=, since ideally you'll only need to set -the fonts once. +Only when there's more than one tab. -#+begin_src emacs-lisp - (defun acdw/setup-fonts () - "Setup fonts. +#+begin_src emacs-lisp :noweb-ref settings + (setq-default tab-bar-show 1) +#+end_src + +** Fonts - This has to happen after the frame is setup for the first time, - so it should be added to `after-focus-change-function'. It - removes itself." +*** Find an installed font from a list of alternatives + +#+begin_src emacs-lisp :noweb-ref functions + (defun set-face-from-alternatives (face frame &rest fontspecs) + "Set FACE on FRAME from first available spec from FONTSPECS. + FACE and FRAME work the same as with `set-face-attribute.'" + (catch :return + (dolist (spec fontspecs) + (when-let ((found (find-font (apply #'font-spec spec)))) + (set-face-attribute face frame :font found) + (throw :return found))))) +#+end_src + +*** Setup fonts on first window focus + +At the end of this function, it removes itself from +=after-focus-change-function=, so it only runs once. + +#+begin_src emacs-lisp :noweb-ref functions + (defun acdw/setup-fonts () + "Setup fonts. This has to happen after the frame is setup for + the first time, so it should be added to `after-focus-change-function'. It + removes itself from that hook." + (interactive) (set-face-from-alternatives 'default nil '(:family "Input Mono" :slant normal @@ -377,9 +284,9 @@ the fonts once. :slant normal :weight normal :height 100)) - ;; `fixed-pitch' should just inherit from `default' + ;; `fixed-pitch' inherits from `default' (set-face-attribute 'fixed-pitch nil :inherit 'default) - + ;; variable-pitch is different (set-face-from-alternatives 'variable-pitch nil '(:family "Input Sans" :slant normal @@ -387,163 +294,318 @@ the fonts once. '(:family "Georgia" :slant normal :weight normal)) - + ;; remove self from hook (remove-function after-focus-change-function #'acdw/setup-fonts)) #+end_src -**** Underlines +Of course, it only makes sense to run the font setup at all if I'm +using the graphical Emacs. -#+begin_src emacs-lisp - (cuss x-underline-at-descent-line t) +#+begin_src emacs-lisp :noweb-ref hooks + (when (display-graphic-p) + (add-function :before after-focus-change-function + #'acdw/setup-fonts)) #+end_src -** Interactivity -*** Dialogs and alerts +*** Underlines -**** Don't use a dialog box +I like the /fancy/ underlines in newer browsers that skip all the +descenders. Emacs doesn't /quite/ have that, but it can put the +underline below all the text. -#+begin_src emacs-lisp - (cuss use-dialog-box nil) +#+begin_src emacs-lisp :noweb-ref settings + (setq-default x-underline-at-descent-line t) #+end_src -**** Yes or no questions +* Interactivity -#+begin_src emacs-lisp +** Dialogs and alerts + +*** Don't use a dialog box + +Ask in the modeline instead. + +#+begin_src emacs-lisp :noweb-ref settings + (setq-default use-dialog-box nil) +#+end_src + +*** Yes or no questions + +I just want to type =y= or =n=, okay? + +#+begin_src emacs-lisp :noweb-ref functions (fset 'yes-or-no-p #'y-or-n-p) #+end_src -**** The Bell +*** The Bell -#+begin_src emacs-lisp - ;; Don't flash the whole screen on bell - (cuss visible-bell nil) +The only system I /sort of/ like the bell on is my Thinkpad, which +does a little on-board speaker beep. Until I can figure out how to +let it do its thing, though, I'll just change the bell on all my +systems. - ;; Instead, flash the mode line - (cuss ring-bell-function #'flash-mode-line) +#+begin_src emacs-lisp :noweb-ref settings + (setq-default visible-bell nil + ring-bell-function #'flash-mode-line) +#+end_src + +**** Flash the mode-line +#+begin_src emacs-lisp :noweb-ref functions (defun flash-mode-line () (invert-face 'mode-line) (run-with-timer 0.2 nil #'invert-face 'mode-line)) #+end_src -t*** Minibuffer +** Minibuffer -*** Minibuffer +*** Keep the cursor away from the minibuffer prompt -**** Keep the cursor away from the minibuffer prompt - -#+begin_src emacs-lisp - (cuss minibuffer-prompt-properties - '(read-only t cursor-intangible t face minibuffer-prompt)) +#+begin_src emacs-lisp :noweb-ref settings + (setq-default minibuffer-prompt-properties + '(read-only t + cursor-intangible t + face minibuffer-prompt)) #+end_src -**** Enable recursive minibuffer +*** Enable a recursive minibuffer -#+begin_src emacs-lisp - (cuss enable-recursive-minibuffers t) +#+begin_src emacs-lisp :noweb-ref + (setq-default enable-recursive-minibuffers t) #+end_src -**** Show how deep the minibuffer goes in the modeline +*** Show the recursivity of the minibuffer in the mode-line -#+begin_src emacs-lisp +#+begin_src emacs-lisp :noweb-ref modes (minibuffer-depth-indicate-mode +1) #+end_src -*** Completing-read +** Completing-read -**** Shadow file names +*** Shadow file names When typing =~= or =/= in the file-selection dialog, Emacs "pretends" -that you've typed them at the beginning of the line. By default, -however, it only /fades out/ the previous contents of the line. I -want to /hide/ those contents. +that you've typed them at the beginning of the line. By default, +however, it only /fades out/ the previous contents of the line. I want +to /hide/ those contents. -#+begin_src emacs-lisp - (cuss file-name-shadow-properties '(invisible t)) +#+begin_src emacs-lisp :noweb-ref settings + (setq-default file-name-shadow-properties '(invisible t)) +#+end_src +#+begin_src emacs-lisp :noweb-ref modes (file-name-shadow-mode +1) #+end_src -**** Ignore case +*** Ignore case -#+begin_src emacs-lisp - (cuss completion-ignore-case t) - (cuss read-buffer-completion-ignore-case t) - (cuss read-file-name-completion-ignore-case t) +#+begin_src emacs-lisp :noweb-ref + (setq-default completion-ignore-case t + read-buffer-completion-ignore-case t + read-file-name-completion-ignore-case t) #+end_src -** Persistence +* Persistence -*** Minibuffer history +** Minibuffer history -The =savehist= package saves the minibuffer history between sessions. -It can also save some other variables alongside the minibuffer -history. Since storage is cheap, I'm also going to keep all my -history. +The =savehist= package saves minibuffer history between sessions, as +well as the option for some other variables. Since storage is cheap, +I keep all of it. -#+begin_src emacs-lisp +#+begin_src emacs-lisp :noweb-ref requires (require 'savehist) +#+end_src - (cuss savehist-additional-variables - '(kill-ring - search-ring - regexp-search-ring)) - - ;; Don't truncate history - (cuss history-length t) - - ;; Delete history duplicates - (cuss history-delete-duplicates t) +#+begin_src emacs-lisp :noweb-ref modes + (setq-default savehist-additional-variables + '(kill-ring + search-ring + regexp-search-ring) + history-length t ; Don't truncate + history-delete-duplicates t) +#+end_src +#+begin_src emacs-lisp :noweb-ref modes (savehist-mode +1) #+end_src -*** File places +** File places -The =saveplace= package saves where I've been in the files I've -visited, so I can return back to them. +The =saveplace= package saves where I've been in my visited files. -#+begin_src emacs-lisp +#+begin_src emacs-lisp :noweb-ref requires (require 'saveplace) +#+end_src - ;; Forget the place in unreadable files - (cuss save-place-forget-unreadable-files t) +Since storage is cheap, but I'm impatient -- especially on Windows -- +I'm not going to check whether the files =save-place= saves the places +of are readable or not. + +#+begin_src emacs-lisp :noweb-ref settings + (setq-default save-place-forget-unreadable-files (when-at :home)) +#+end_src +#+begin_src emacs-lisp :noweb-ref modes (save-place-mode +1) #+end_src -*** Recent files +** Recent files -#+begin_src emacs-lisp +I also like to keep track of recently-opened files. =recentf= helps +with that. + +#+begin_src emacs-lisp :noweb-ref requires (require 'recentf) +#+end_src - ;; Limit the number of items in the recentf menu - (cuss recentf-max-menu-items 100) - ;; But not the number of items in the actual list - (cuss recentf-max-saved-items nil) +#+begin_src emacs-lisp :noweb-ref settings + (setq-default recentf-max-menu-items 100 + recentf-max-saved-items nil) +#+end_src +#+begin_src emacs-lisp :noweb-ref modes (recentf-mode +1) #+end_src -**** Save the recentf list periodically +*** Save the recentf list periodically -#+begin_src emacs-lisp - (defun acdw/maybe-save-recentf () - "Save `recentf-file every five minutes, but only when out of focus." +#+begin_src emacs-lisp :noweb-ref functions + (defun maybe-save-recentf () + "Save `recentf-file' every five minutes, but only when out of focus." (defvar recentf--last-save (time-convert nil 'integer) - "When we last ran `recentf-save-list'.") + "When we last saved the `recentf-save-list'.") (when (> (time-convert (time-since recentf--last-save) 'integer) (* 60 5)) - (setq recentf--last-save (time-convert nil 'integer)) - (acdw/when-unfocused #'recentf-save-list))) + (setq-default recentf--last-save (time-convert nil 'integer)) + (when-unfocused #'recentf-save-list))) +#+end_src + +#+begin_src emacs-lisp :noweb-ref hooks + (add-function :after after-focus-change-function + #'maybe-save-recentf) +#+end_src + +* Responsiveness + +Emacs has a slew of well-documented problems with snappiness. +Luckily, there are a number of solutions. + +** Only do things when unfocused + +Sometimes, we can fake responsiveness by only performing commands when +the user is looking at something else. + +#+begin_src emacs-lisp :noweb-ref functions + (defun when-unfocused (func &rest args) + "Run FUNC, with ARGS, iff all frames are out of focus." + (when (seq-every-p #'null (mapcar #'frame-focus-state (frame-list))) + (apply func args))) +#+end_src + +* Files + +** Encoding + +*** UTF-8 + +It's 2020. Let's encode files like it is. + +#+begin_src emacs-lisp :noweb-ref settings + (prefer-coding-system 'utf-8) + (set-default-coding-systems 'utf-8) + (set-terminal-coding-system 'utf-8) + (set-keyboard-coding-system 'utf-8) + + (setq-default buffer-file-coding-system 'utf-8 + x-select-request-type '(UTF8_STRING COMPOUND_TEXT TEXT STRING)) +#+end_src + +*** UNIX-style line endings + +This function is from the [[https://www.emacswiki.org/emacs/EndOfLineTips][Emacs Wiki]]. + +#+begin_src emacs-lisp :noweb-ref functions + (defun ewiki/no-junk-please-were-unixish () + "Convert line endings to UNIX, dammit." + (let ((coding-str (symbol-name buffer-file-coding-system))) + (when (string-match "-\\(?:dos\\|mac\\)$" coding-str) + (set-buffer-file-coding-system 'unix)))) +#+end_src + +I add it to both =file-find-hook= /and/ =before-save-hook= because I'm +/that/ over it. I don't want to ever work with anything other than +UNIX line endings ever again. I just don't care. Even Microsoft +Notepad can handle UNIX line endings, so I don't want to hear it. + +#+begin_src emacs-lisp :noweb-ref hooks + (add-hook 'find-file-hook #'ewiki/no-junk-please-were-unixish) + (add-hook 'before-save-hook #'ewiki/no-junk-please-were-unixish) +#+end_src + +** Keep =~/.emacs.d= clean :package: - (add-function :after after-focus-change-function #'acdw/maybe-save-recentf) +* Package management :package: +:PROPERTIES: +:header-args: :noweb-ref package-bootstrap +:END: + +Emacs is the /extensible/ editor, and that means I want to use +third-party packages. Of course, first I have to /manage/ those +packages. I use the excellent =straight.el=. + +** TODO Update the PATH + +** Disable =package.el= + +#+begin_src emacs-lisp + (setq package-enable-at-startup nil) #+end_src -** Files +** Bootstrap -*** Encoding +The following is straight (heh) from the straight repo, wrapped in a +function so I can call it in another wrapper. + +#+begin_src emacs-lisp + (defun acdw/bootstrap-straight () + "Bootstrap straight.el." + (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 + (concat + "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))) +#+end_src + +To actually bootstrap straight, I'll first try running the above +directly. If it errors (it tends to on Windows), I'll directly clone +the repo using git, /then/ run the bootstrap code. + +#+begin_src emacs-lisp + (unless (ignore-errors (acdw/bootstrap-straight)) + (let ((msg "Straight.el didn't bootstrap correctly. Cloning directly")) + (message "%s..." msg) + (call-process "git" nil + (get-buffer-create "*bootstrap-straight-messages*") nil + "clone" + "https://github.com/raxod502/straight.el" + (expand-file-name "straight/repos/straight.el" + user-emacs-directory)) + (message "%s...Done." msg) + (acdw/bootstrap-straight))) +#+end_src * System-specific @@ -552,27 +614,41 @@ easier to use in both systems, I've included various system-specific settings and written some ancillary scripts. ** Determine where I am +:PROPERTIES: +:header-args: :noweb-ref functions +:END: #+begin_src emacs-lisp (defmacro when-at (conditions &rest commands) - "Run COMMANDS when at a specific place. + "Run COMMANDS, or let the user know, when at a specific place. CONDITIONS are one of `:work', `:home', or a list beginning with those and other conditions to check. COMMANDS are only run if - all CONDITIONS are met." + all CONDITIONS are met. + + If COMMANDS is empty or nil, simply return the result of CONDITIONS." (declare (indent 1)) - (let ((at-work (memq system-type '(ms-dos windows-nt))) - (at-home (memq system-type '(gnu gnu/linux gnu/kfreebsd)))) + (let ((at-work '(memq system-type '(ms-dos windows-nt))) + (at-home '(memq system-type '(gnu gnu/linux gnu/kfreebsd)))) (pcase conditions - (:work `(when ',at-work ,@commands)) - (:home `(when ',at-home ,@commands)) - (`(:work ,others) `(when (and ',at-work ,others) - ,@commands)) - (`(:home ,others) `(when (and ',at-home ,others) - ,@commands))))) -#+end_src - -** Linux + (:work (if commands `(when ,at-work ,@commands) at-work)) + (:home (if commands `(when ,at-home ,@commands) at-home)) + ((guard (eq (car conditions) :work)) + (if commands + `(when (and ,at-work ,@(cdr conditions)) + ,@commands) + `(and ,at-work ,@(cdr conditions)))) + ((guard (eq (car conditions) :home)) + (if commands + `(when (and ,at-home ,@(cdr conditions)) + ,@commands) + `(and ,at-work ,@(cdr conditions))))))) +#+end_src + +** Linux (home) +:PROPERTIES: +:header-args: :noweb-ref linux-specific +:END: *** Settings @@ -580,14 +656,14 @@ settings and written some ancillary scripts. **** em :PROPERTIES: -:header-args: :tangle-mode (identity #o755) :tangle bin/em :mkdirp yes +:header-args: :tangle-mode (identity #o755) :mkdirp yes :END: Here's a wrapper script that'll start =emacs --daemon= if there isn't one, and then launch =emacsclient= with the arguments. Install it to your =$PATH= somewhere. -#+begin_src sh :shebang "#!/bin/sh" +#+begin_src sh :shebang "#!/bin/sh" :tangle (when-at :home "~/bin/em") if ! emacsclient -nc "$@"; then emacs --daemon emacsclient -nc "$@" @@ -596,13 +672,13 @@ your =$PATH= somewhere. **** emacsclient.desktop :PROPERTIES: -:header-args: :tangle bin/emacsclient.desktop :mkdirp yes +:header-args: :mkdirp yes :END: I haven't really tested this yet, but it should allow me to open other files and things in Emacs. From [[https://www.taingram.org/blog/emacs-client.html][taingram]]. -#+begin_src conf-desktop +#+begin_src conf-desktop :tangle (when-at :home "~/.local/share/applications/emacsclient.desktop") [Desktop Entry] Name=Emacs Client GenericName=Text Editor @@ -615,7 +691,10 @@ files and things in Emacs. From [[https://www.taingram.org/blog/emacs-client.ht Categories=Utility;TextEditor; #+end_src -** Windows +** Windows (work) +:PROPERTIES: +:header-args: :noweb-ref windows-specific +:END: I use Windows at work, where I /also/ don't have Admin rights. So I kind of fly-by-night there. Much of the ideas and scripts in this @@ -623,24 +702,21 @@ section come from [[https://github.com/termitereform/JunkPile/blob/master/emacs- *** Settings -#+NAME: w32-settings #+begin_src emacs-lisp - (when (memq system-type '(windows-nt ms-dos cygwin)) - (setq w32-allow-system-shell t ; enable cmd.exe as shell - )) + (setq-default w32-allow-system-shell t) ; enable cmd.exe as shell #+end_src *** Scripts :PROPERTIES: -:header-args: :noweb tangle :mkdirp yes +:header-args: :noweb yes :mkdirp yes :END: **** Common variables #+NAME: w32-bat-common #+begin_src bat -set HOME=%~dp0..\..\ -set EMACS=%~dp0..\..\..\bin\runemacs.exe +set HOME=%~dp0..\.. +set EMACS=%HOME%\Applications\Emacs\bin\runemacs.exe #+end_src **** Emacs Daemon @@ -649,9 +725,9 @@ Either run this once at startup, or put a shortcut of it in the Startup folder: =%USERPROFILE%\AppData\Roaming\Microsoft\Windows\Start Menu\Programs\Startup=. -#+begin_src bat :tangle "bin/Emacs Daemon.cmd" +#+begin_src bat :tangle (when-at :work "~/Applications/cmd/Emacs Daemon.cmd") <> -"%EMACS%" --daemon +%EMACS% --daemon #+end_src **** Emacs Client @@ -661,9 +737,9 @@ run =runemacs.exe=. *This is the main shortcut for running Emacs.* -#+begin_src bat :tangle bin/Emacs.cmd +#+begin_src bat :tangle (when-at :work "~/Applications/cmd/Emacs.cmd") <> -set EMACSC=%~dp0..\..\..\bin\emacsclientw.exe +set EMACSC=%HOME%\Applications\Emacs\bin\emacsclientw.exe "%EMACSC%" -n -c -a "%EMACS%" %* #+end_src @@ -671,7 +747,7 @@ set EMACSC=%~dp0..\..\..\bin\emacsclientw.exe This runs Emacs with the factory settings. -#+begin_src bat :tangle "bin/Emacs Safe Start.cmd" +#+begin_src bat :tangle (when-at :work "~/Applications/cmd/Emacs Safe Start.cmd") <> "%EMACS%" -Q %* #+end_src @@ -680,23 +756,76 @@ This runs Emacs with the factory settings. This runs Emacs with the =--debug-init= option enabled. -#+begin_src bat :tangle "bin/Emacs Debug.cmd" +#+begin_src bat :tangle (when-at :work "~/Applications/cmd/Emacs Debug.cmd") <> "%EMACS%" --debug-init %* #+end_src * Appendices + +** =config.el= +:PROPERTIES: +:header-args: :tangle config.el :noweb yes +:END: + +While =config.el= is written above, I use Noweb references to tangle +them all together in the following block, which enables me to organize +my config here /logically/, while keeping the generated file organized +/programmatically/. + +*** Enable lexical binding + +#+begin_src emacs-lisp + ;; config.el -*- lexical-binding: t -*- +#+end_src + +*** Disclaimer +:PROPERTIES: +:header-args: :noweb-ref disclaimer +:END: + +#+begin_src emacs-lisp + ;; This file is automatically tangled from config.org. + ;; Hand edits will be overwritten! +#+end_src + +*** The rest + +#+begin_src emacs-lisp + ;;; REQUIRES + <> + ;;; PACKAGES + <> + ;;; FUNCTIONS + <> + ;;; SETTINGS + <> + ;;; MODES + <> + ;;; SYSTEM-DEPENDENT SETTINGS + (when-at :home + <> + ) + (when-at :work + <> + ) + ;;; HOOKS + <> + ;;; BINDINGS + <> +#+end_src + ** Emacs's files *** init.el :PROPERTIES: -:header-args: :tangle init.el :comments both +:header-args: :tangle init.el :noweb yes :END: The classic Emacs initiation file. **** Use lexical binding when evaluating =init.el= -#+begin_src emacs-lisp :comments no :noweb tangle +#+begin_src emacs-lisp ;; init.el -*- lexical-binding: t -*- <> #+end_src @@ -704,7 +833,7 @@ The classic Emacs initiation file. **** Prefer newer files to older files #+begin_src emacs-lisp - (setq load-prefer-newer t) + (setq-default load-prefer-newer t) #+end_src **** Load the config @@ -715,27 +844,33 @@ directly from Org if it's newer. #+begin_src emacs-lisp (let* (;; Speed up init - (gc-cons-threshold most-positive-fixnum) - (file-name-handler-alist nil) - ;; Config file names - (conf (expand-file-name "config" - user-emacs-directory)) - (conf-el (concat conf ".el")) - (conf-org (concat conf ".org"))) - (unless (and (file-newer-than-file-p conf-el conf-org) - (load conf 'no-error)) - ;; A plain require here just loads the older `org' - ;; in Emacs' install dir. We need to add the newer - ;; one to the `load-path', hopefully that's all. - (add-to-list 'load-path (expand-file-name "straight/build/org" - user-emacs-directory)) - (require 'org) - (org-babel-load-file conf-org))) + (gc-cons-threshold most-positive-fixnum) + (file-name-handler-alist nil) + ;; Config file names + (config (expand-file-name "config" + user-emacs-directory)) + (config.el (concat config ".el")) + (config.org (concat config ".org")) + (straight-org-dir (expand-file-name "straight/build/org" + user-emacs-directory))) + ;; Unless config.org is /newer/ than config.el, *or* the config + ;; is able to be loaded without errors, load the config from + ;; config.org. + (unless (or (file-newer-than-file-p config.org config.el) + (load config 'no-error)) + ;; A plain require here just loads the older `org' + ;; in Emacs' install dir. We need to add the newer + ;; one to the `load-path', hopefully that's all. + (when (file-exists-p straight-org-dir) + (add-to-list 'load-path straight-org-dir)) + ;; Load config.org + (require 'org) + (org-babel-load-file config.org))) #+end_src *** early-init.el :PROPERTIES: -:header-args: :tangle early-init.el :comments both +:header-args: :tangle early-init.el :noweb yes :END: Beginning with 27.1, Emacs also loads an =early-init.el= file, before @@ -744,29 +879,31 @@ little as possible in this file, so I only have what I need. **** Don't byte-compile this file -#+begin_src emacs-lisp :comments no :noweb tangle +#+begin_src emacs-lisp ;; early-init.el -*- no-byte-compile: t; -*- <> #+end_src -**** Disable loading of =package.el= +**** Package management -I use =straight.el= instead. +Since =early-init.el= loads before the built-in package manager +initializes, I disable it and bootstrap my package manager of choice, +=straight.el=. #+begin_src emacs-lisp - (setq package-enable-at-startup nil) + <> #+end_src **** Don't resize the frame when loading fonts #+begin_src emacs-lisp - (setq frame-inhibit-implied-resize t) + (setq-default frame-inhibit-implied-resize t) #+end_src **** Resize frame by pixels #+begin_src emacs-lisp - (setq frame-resize-pixelwise t) + (setq-default frame-resize-pixelwise t) #+end_src **** Shoe-horned from elsewhere in =config.org= @@ -778,13 +915,13 @@ weird shoe-horning of other bits of my config here, in a backwater heading in an appendix, isn't quite the future I wanted. But it's what I have for now. -#+begin_src emacs-lisp :noweb tangle +#+begin_src emacs-lisp <> #+end_src ** License :PROPERTIES: -:header-args: :tangle LICENSE :comments no +:header-args: :tangle LICENSE :END: Copyright © 2020 Case Duckworth @@ -817,3 +954,33 @@ It's highly likely that the WTFPL is completely incompatible with the GPL, for what should be fairly obvious reasons. To that, I say: *SUE ME, RMS!* + +** Make it easier to edit =config.org= :noexport: + +Because I use a lot of =:noweb-ref= directives in this file, editing +it can be annoying -- I need some nice templates. Run this code block +with =C-c C-c=. + +#+begin_src emacs-lisp :results output silent + (require 'org-tempo) + + (dolist (cell '(("el" . "src emacs-lisp") + ("cr" . "src emacs-lisp :noweb-ref requires") + ("cf" . "src emacs-lisp :noweb-ref functions") + ("cs" . "src emacs-lisp :noweb-ref settings") + ("cm" . "src emacs-lisp :noweb-ref modes") + ("cl" . "src emacs-lisp :noweb-ref linux-specific") + ("cw" . "src emacs-lisp :noweb-ref windows-specific") + ("cp" . "src emacs-lisp :noweb-ref packages") + ("ch" . "src emacs-lisp :noweb-ref hooks") + ("cb" . "src emacs-lisp :noweb-ref bindings"))) + (add-to-list 'org-structure-template-alist cell)) +#+end_src + +** Local variables :noexport: + +# Local variables: +# org-adapt-indentation: nil +# lisp-indent-function: 'common-lisp-indent-function +# eval: (auto-fill-mode +1) +# End: -- cgit 1.4.1-21-gabe81