From d7b7145461d3537052e212b9251de5b559770d32 Mon Sep 17 00:00:00 2001 From: Case Duckworth Date: Tue, 19 Jan 2021 23:44:04 -0600 Subject: Remove README.md --- README.md | 2211 ------------------------------------------------------------- 1 file changed, 2211 deletions(-) delete mode 100644 README.md diff --git a/README.md b/README.md deleted file mode 100644 index 1f56e6c..0000000 --- a/README.md +++ /dev/null @@ -1,2211 +0,0 @@ - - -# Basics - - -## Disclaimer - - ;; config.el -*- lexical-binding: t -*- - ;; This file is automatically tangled from config.org. - ;; Hand edits will be overwritten! - - -## About me - - (setq user-full-name "Case Duckworth" - user-mail-address "acdw@acdw.net") - - -## Correct `exec-path` - -Straight depends on Git, so I need to tell Emacs where different paths are. - - (let ((win-downloads "c:/Users/aduckworth/Downloads")) - (dolist (path (list - ;; Linux - (expand-file-name "bin" - user-emacs-directory) - (expand-file-name "~/bin") - (expand-file-name "~/.local/bin") - (expand-file-name "~/Scripts") - ;; Windows - (expand-file-name "emacs/bin" - win-downloads) - (expand-file-name "m/usr/bin" - win-downloads) - (expand-file-name "m/mingw64/bin" - win-downloads) - (expand-file-name "PortableGit/bin" - win-downloads) - (expand-file-name "PortableGit/usr/bin" - win-downloads))) - (when (file-exists-p path) - (add-to-list 'exec-path path)))) - - -## Package management - - -### Straight.el - -Straight can't bootstrap itself on Windows, so I've wrapped the -bootstrap code from straight's repo in a function. - - (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))) - -Now, I'll *try* running it regular-style, ignoring the errors. If it -doesn't work, I'll call git directly and clone the repo myself. - - (unless (ignore-errors (acdw/bootstrap-straight)) - (message "%s" "Straight.el didn't bootstrap correctly. Cloning directly...") - (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)) - (acdw/bootstrap-straight)) - - -## Ease-of-configuring functions - - -### Emulate use-package’s `:custom` - - (defmacro cuss (var val &optional _docstring) - "Basically, `:custom' from `use-package', but without `use-package'." - (declare (doc-string 3) - (indent 2)) - `(funcall (or (get ',var 'custom-set) #'set-default) - ',var ,val)) - - -### Emulate use-package’s `:custom-face`, but better - - (defvar acdw--custom-faces () - "List of custom faces to run through acdw/set-custom-faces.") - - (defun acdw/set-custom-faces () - "Run `customize-set-faces' on `acdw--custom-faces'." - (message "%s" "Customizing faces...") - (apply #'custom-set-faces acdw--custom-faces)) - - (defun cussface (spec) - "Add SPEC to `acdw--custom-faces', and add a hook to run - `acdw/set-custom-faces' after init." - (add-to-list 'acdw--custom-faces spec) - (add-hook 'after-init-hook #'acdw/set-custom-faces)) - - -### Determine whether any Emacs frame is focused or not - -This comes in handy when I want to garbage collect, say, or save recent files. - - (defun acdw/when-unfocused (func &rest args) - "Run FUNC with ARGS only if all frames are out of focus." - (if (seq-every-p #'null (mapcar #'frame-focus-state (frame-list))) - (apply func args))) - - -### Determine where I am - -I use Emacs at home, with Linux, and at work, with Windows. - - (defmacro when-at (conditions &rest commands) - "Only do COMMANDS when CONDITIONS are met. - CONDITIONS are one of `:work', `:home', or a list beginning with - the above and other conditions to check." - (declare (indent 1)) - (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))))) - - -## Clean `.emacs.d` - - (straight-use-package 'no-littering) - (require 'no-littering) - - -### Don’t clutter `init.el` with customizations - - (with-eval-after-load 'no-littering - (cuss custom-file (no-littering-expand-etc-file-name "custom.el"))) - - -## Look and feel - - -### Cursor - - (cuss cursor-type 'bar - "Show a vertical bar for the cursor.") - - (cuss cursor-in-non-selected-windows 'hbar - "Show an empty box in inactive windows.") - - ;; Don't blink the cursor - (blink-cursor-mode -1) - - -### Tool Bars - - -#### Tool bars and menu bars - - (menu-bar-mode -1) - (tool-bar-mode -1) - - -#### Scroll bars - - (scroll-bar-mode -1) - (horizontal-scroll-bar-mode -1) - - -### Dialogs - - (cuss use-dialog-box nil - "Don't use dialog boxes to ask questions.") - - -#### Yes or no questions - - (fset 'yes-or-no-p #'y-or-n-p) - - -#### The Bell - -from [EmacsWiki](https://www.emacswiki.org/emacs/AlarmBell#h5o-3). - - (setq visible-bell nil - ring-bell-function 'flash-mode-line) - - (defun flash-mode-line () - (invert-face 'mode-line) - (run-with-timer 0.1 nil #'invert-face 'mode-line)) - - -### Frames - - -#### Frame titles - - (cuss frame-title-format (concat invocation-name "@" system-name - ": %b %+%+ %f")) - - -#### Fringes - - (cuss indicate-empty-lines t - "Show an indicator on the left fringe of empty lines past the - end of the buffer.") - (cuss indicate-buffer-boundaries 'right - "Indicate the beginning and end of the buffer and whether it - scrolls off-window in the right fringe.") - - (cuss visual-line-fringe-indicators '(left-curly-arrow nil) - "Indicate continuing lines with a curly arrow in the left fringe.") - - ;; redefine the `left-curly-arrow' indicator to be less distracting. - (define-fringe-bitmap 'left-curly-arrow - [#b11000000 - #b01100000 - #b00110000 - #b00011000]) - - -#### Minibuffer - - (cuss minibuffer-prompt-properties - '(read-only t cursor-intangible t face minibuffer-prompt) - "Keep the cursor away from the minibuffer prompt.") - - -#### Tabs - - (cuss tab-bar-tab-name-function - #'tab-bar-tab-name-current-with-count - "Show the tab name as the name of the current buffer, plus a - count of the windows in the tab.") - - (cuss tab-bar-show 1 - "Show the tab bar, when there's more than one tab.") - - -### Windows - - -#### Winner mode - - (when (fboundp 'winner-mode) - (winner-mode +1)) - - -#### Switch windows or buffers if one window - -from [u/astoff1](https://www.reddit.com/r/emacs/comments/kz347f/what_parts_of_your_config_do_you_like_best/gjlnp2c/). - - (defun acdw/other-window-or-buffer () - "Switch to other window, or previous buffer." - (interactive) - (if (eq (count-windows) 1) - (switch-to-buffer nil) - (other-window 1))) - - (global-set-key (kbd "M-o") #'acdw/other-window-or-buffer) - - -#### Pop-up windows - - (straight-use-package 'popwin) - (popwin-mode +1) - - -### Buffers - - -#### Uniquify buffers - - (require 'uniquify) - (cuss uniquify-buffer-name-style 'forward - "Uniquify buffers' names by going up the path trees until they - become unique.") - - -#### Startup buffers - - (cuss inhibit-startup-screen t - "Don't show Emacs' startup buffer.") - - (cuss initial-buffer-choice t - "Start with *scratch*.") - - (cuss initial-scratch-message "" - "Empty *scratch* buffer.") - - -#### Kill the current buffer - - (defun acdw/kill-a-buffer (&optional prefix) - "Kill a buffer based on the following rules: - - C-x k ⇒ Kill current buffer & window - C-u C-x k ⇒ Kill OTHER window and its buffer - C-u C-u C-x C-k ⇒ Kill all other buffers and windows - - Prompt only if there are unsaved changes." - (interactive "P") - (pcase (or (car prefix) 0) - ;; C-x k ⇒ Kill current buffer & window - (0 (kill-current-buffer) - (unless (one-window-p) (delete-window))) - ;; C-u C-x k ⇒ Kill OTHER window and its buffer - (4 (other-window 1) - (kill-current-buffer) - (unless (one-window-p) (delete-window))) - ;; C-u C-u C-x C-k ⇒ Kill all other buffers and windows - (16 (mapc 'kill-buffer (delq (current-buffer) (buffer-list))) - (delete-other-windows)))) - - (define-key ctl-x-map "k" #'acdw/kill-a-buffer) - - -##### Remap `C-x M-k` to bring up the buffer-killing menu - - (define-key ctl-x-map (kbd "M-k") #'kill-buffer) - - -#### Immortal `*scratch*` buffer - - (defun immortal-scratch () - (if (eq (current-buffer) (get-buffer "*scratch*")) - (progn (bury-buffer) - nil) - t)) - - (add-hook 'kill-buffer-query-functions 'immortal-scratch) - - -### Modeline - - -#### Smart mode line - - (straight-use-package 'smart-mode-line) - - (cuss sml/no-confirm-load-theme t - "Pass the NO-CONFIRM flag to `load-theme'.") - - (sml/setup) - - -#### Rich minority - -Since this *comes* with smart mode line, I’m just going to use it, -instead of `diminish` or another package. I do have to write this -helper function, though, to add things to the whitelist. - - (defun rm/whitelist-add (regexp) - "Add a REGEXP to the whitelist for `rich-minority'." - (if (listp 'rm--whitelist-regexps) - (add-to-list 'rm--whitelist-regexps regexp) - (setq rm--whitelist-regexps `(,regexp))) - (setq rm-whitelist - (mapconcat 'identity rm--whitelist-regexps "\\|"))) - - (straight-use-package 'rich-minority) - - (rm/whitelist-add "^$") - - -### Theme - - -#### Modus Themes - - (straight-use-package 'modus-themes) - - (cuss modus-themes-slanted-constructs t - "Use more slanted constructs.") - (cuss modus-themes-bold-constructs t - "Use more bold constructs.") - - (cuss modus-themes-region 'bg-only - "Only highlight the background of the selected region.") - - (cuss modus-themes-org-blocks 'grayscale - "Show org-blocks with a grayscale background.") - (cuss modus-themes-headings - '((1 . line) - (t . t)) - "Highlight top headings with `line' style, and others by default.") - - (cuss modus-themes-scale-headings t - "Scale headings by the ratios below.") - (cuss modus-themes-scale-1 1.1) - (cuss modus-themes-scale-2 1.15) - (cuss modus-themes-scale-3 1.21) - (cuss modus-themes-scale-4 1.27) - (cuss modus-themes-scale-5 1.33) - - (load-theme 'modus-operandi t) - - -#### Change themes based on time of day - - (cuss calendar-latitude 30.4515) - (cuss calendar-longitude -91.1871) - - (defun acdw/run-with-sun (sunrise-command sunset-command) - "Run commands at sunrise and sunset." - (let* ((times-regex (rx (* nonl) - (: (any ?s ?S) "unrise") " " - (group (repeat 1 2 digit) ":" - (repeat 1 2 digit) - (: (any ?a ?A ?p ?P) (any ?m ?M))) - (* nonl) - (: (any ?s ?S) "unset") " " - (group (repeat 1 2 digit) ":" - (repeat 1 2 digit) - (: (any ?a ?A ?p ?P) (any ?m ?M))) - (* nonl))) - (ss (sunrise-sunset)) - (_m (string-match times-regex ss)) - (sunrise-time (match-string 1 ss)) - (sunset-time (match-string 2 ss))) - (run-at-time sunrise-time (* 60 60 24) sunrise-command) - (run-at-time sunset-time (* 60 60 24) sunset-command))) - - (acdw/run-with-sun #'modus-themes-load-operandi #'modus-themes-load-vivendi) - - -### Fonts - - -#### Define fonts - - (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))))) - - (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 `window-setup-hook'. It - removes itself from that hook." - (interactive) - (when (display-graphic-p) - (dolist (face '(default fixed-pitch)) - ;; fixed-pitch /is/ the default - (set-face-from-alternatives face nil - '(:family "Input Mono" - :slant normal - :weight normal - :height 110) - '(:family "Go Mono" - :slant normal - :weight normal - :height 100) - '(:family "Consolas" - :slant normal - :weight normal - :height 100))) - ;; variable-pitch is different - (set-face-from-alternatives 'variable-pitch nil - '(:family "Input Sans" - :slant normal - :weight normal) - '(:family "Georgia" - :slant normal - :weight normal))) - - ;; remove myself from the hook - (remove-function after-focus-change-function #'acdw/setup-fonts)) - - (add-function :before after-focus-change-function #'acdw/setup-fonts) - - -#### Custom faces - - (cussface '(font-lock-comment-face - ((t (:inherit (custom-comment italic variable-pitch)))))) - - -#### Line spacing - - (cuss line-spacing 0.1 - "Add 10% extra space below each line.") - - -#### Underlines - - (cuss x-underline-at-descent-line t - "Draw the underline at the same place as the descent line.") - - -#### Unicode Fonts - - (straight-use-package 'unicode-fonts) - (require 'unicode-fonts) - (unicode-fonts-setup) - - -## Interactivity - - -### Completing read - - -#### Shadow file names in `completing-read`. - - (cuss file-name-shadow-properties '(invisible t)) - - (file-name-shadow-mode +1) - - -#### Ignore case in `completing-read` - - (cuss completion-ignore-case t) - (cuss read-buffer-completion-ignore-case t) - (cuss read-file-name-completion-ignore-case t) - - -#### Minibuffer recursivity - - (cuss enable-recursive-minibuffers t) - (minibuffer-depth-indicate-mode +1) - - -#### Selectrum - - (straight-use-package 'selectrum) - (require 'selectrum) - (selectrum-mode +1) - - -#### Prescient - - (straight-use-package 'prescient) - (require 'prescient) - (prescient-persist-mode +1) - - (straight-use-package 'selectrum-prescient) - (require 'selectrum-prescient) - (selectrum-prescient-mode +1) - - -#### Consult - - (straight-use-package '(consult - :host github - :repo "minad/consult" - :files (:defaults "consult-pkg.el"))) - (require 'consult) - - (with-eval-after-load 'consult - (define-key ctl-x-map "b" #'consult-buffer) - (define-key ctl-x-map (kbd "C-r") #'consult-buffer) - (define-key ctl-x-map "4b" #'consult-buffer-other-window) - (define-key ctl-x-map "5b" #'consult-buffer-other-frame) - - (define-key goto-map "o" #'consult-outline) - (define-key goto-map "g" #'consult-line) - (define-key goto-map (kbd "M-g") #'consult-line) - (define-key goto-map "l" #'consult-line) - (define-key goto-map "m" #'consult-mark) - (define-key goto-map "i" #'consult-imenu) - (define-key goto-map "e" #'consult-error) - - (global-set-key (kbd "M-y") #'consult-yank-pop) - - (define-key help-map "a" #'consult-apropos) - - (fset 'multi-occur #'consult-multi-occur)) - - -#### Marginalia - - (straight-use-package '(marginalia - :host github - :repo "minad/marginalia" - :branch "main")) - - (cuss marginalia-annotators - '(marginalia-annotators-heavy - marginalia-annotators-light)) - - (marginalia-mode +1) - - -### Completion - - (global-set-key (kbd "M-/") #'hippie-expand) - - -### Garbage collection - - (straight-use-package 'gcmh) - (gcmh-mode +1) - - (defun dotfiles--gc-on-last-frame-out-of-focus () - "GC if all frames are inactive." - (if (seq-every-p #'null (mapcar #'frame-focus-state (frame-list))) - (garbage-collect))) - - (add-function :after after-focus-change-function - #'dotfiles--gc-on-last-frame-out-of-focus) - - -## Keyboard - - -### `ESC` cancels all - - (global-set-key (kbd "") #'keyboard-escape-quit) - - -### Personal prefix key: `C-z` - - (defvar acdw/map - (let ((map (make-sparse-keymap)) - (c-z (global-key-binding "\C-z"))) - (global-unset-key "\C-z") - (define-key global-map "\C-z" map) - (define-key map "\C-z" c-z) - map)) - - (run-hooks 'acdw/map-defined-hook) - - -### Show keybindings - - (straight-use-package 'which-key) - (which-key-mode +1) - - -## Mouse - - -### Preserve screen position when scrolling with the mouse wheel - -from [u/TheFrenchPoulp](https://www.reddit.com/r/emacs/comments/km9by4/weekly_tipstricketc_thread/ghg2c9d/). - - (advice-add 'mwheel-scroll :around #'me/mwheel-scroll) - - (defun me/mwheel-scroll (original &rest arguments) - "Like `mwheel-scroll' but preserve screen position. - See `scroll-preserve-screen-position'." - (let ((scroll-preserve-screen-position :always)) - (apply original arguments))) - - -### Scroll much faster - -from [mpereira](https://github.com/mpereira/.emacs.d#make-cursor-movement-an-order-of-magnitude-faster), from somewhere else. - - (cuss auto-window-vscroll nil - "Don't auto-adjust `window-vscroll' to view long lines.") - - (cuss fast-but-imprecise-scrolling t - "Scroll fast, but possibly with inaccurate text rendering.") - - (cuss jit-lock-defer-time 0 - "Only defer font-locking when input is pending.") - - -## Persistence - - -### Minibuffer history - - (require 'savehist) - - (cuss savehist-additional-variables - '(kill-ring - search-ring - regexp-search-ring) - "Other variables to save alongside the minibuffer history.") - - (cuss history-length t - "Don't truncate history.") - - (cuss history-delete-duplicates t - "Delete history duplicates.") - - (savehist-mode +1) - - -### File places - - (require 'saveplace) ; this isn't required, but ... I like having it here - - (cuss save-place-forget-unreadable-files t - "Don't check if files are readable or not.") - - (save-place-mode +1) - - -### Recent files - - (require 'recentf) - - (cuss recentf-max-menu-items 100 - "The maximum number of items in the recentf menu.") - (cuss recentf-max-saved-items nil - "Don't limit the number of recent files.") - - (with-eval-after-load 'no-littering - (add-to-list 'recentf-exclude no-littering-var-directory) - (add-to-list 'recentf-exclude no-littering-etc-directory)) - - (recentf-mode +1) - - ;; save recentf list when focusing away - (defun acdw/maybe-save-recentf () - "Save `recentf-file' when out of focus, but only if we haven't - in five minutes." - (defvar recentf-last-save (time-convert nil 'integer) - "How long it's been since we last saved the recentf 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))) - - (add-function :after after-focus-change-function - #'acdw/maybe-save-recentf) - - -## Undo - - (straight-use-package 'undo-fu) - (require 'undo-fu) - - (global-set-key (kbd "C-/") #'undo-fu-only-undo) - (global-set-key (kbd "C-?") #'undo-fu-only-redo) - - (straight-use-package 'undo-fu-session) - (require 'undo-fu-session) - - (cuss undo-fu-session-incompatible-files - '("/COMMIT_EDITMSG\\'" - "/git-rebase-todo\\'") - "A list of files that are incompatible with the concept of undo sessions.") - - (with-eval-after-load 'no-littering - (let ((dir (no-littering-expand-var-file-name "undos"))) - (make-directory dir 'parents) - (cuss undo-fu-session-directory dir))) - - (global-undo-fu-session-mode +1) - - -## Files - - -### Encoding - - -#### UTF-8 - -from [Mastering Emacs](https://www.masteringemacs.org/article/working-coding-systems-unicode-emacs). - - (prefer-coding-system 'utf-8) - (set-default-coding-systems 'utf-8) - (set-terminal-coding-system 'utf-8) - (set-keyboard-coding-system 'utf-8) - ;; backwards compatibility: - ;; `default-buffer-file-coding-system' is deprecated in 23.2. - (setq default-buffer-file-coding-system 'utf-8) - - ;; Treat clipboard as UTF-8 string first; compound text next, etc. - (setq x-select-request-type '(UTF8_STRING COMPOUND_TEXT TEXT STRING)) - - -#### Convert all files to UNIX-style line endings - -from [Emacs Wiki](https://www.emacswiki.org/emacs/EndOfLineTips). - - (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)))) - -I add it to the `find-file-hook` *and* `before-save-hook` because 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. - - (add-hook 'find-file-hook #'ewiki/no-junk-please-were-unixish) - (add-hook 'before-save-hook #'ewiki/no-junk-please-were-unixish) - - -### Backups - - (cuss backup-by-copying 1) - (cuss delete-old-versions -1) - (cuss version-control t) - (cuss vc-make-backup-files t) - - (with-eval-after-load 'no-littering - (let ((dir (no-littering-expand-var-file-name "backup"))) - (make-directory dir 'parents) - (cuss backup-directory-alist - `((".*" . ,dir))))) - - -### Auto-saves - - ;; turn off auto-save-mode until we can set up the right directory for them - (auto-save-mode -1) - - (with-eval-after-load 'no-littering - (let ((dir (no-littering-expand-var-file-name "autosaves"))) - (make-directory dir 'parents) - (cuss auto-save-file-name-transforms - `((".*" ,dir t)))) - - (auto-save-mode +1)) - - -### Super-save - -Because I like *overkill*, or at least … over-*saving*. - - (straight-use-package 'super-save) - - (cuss super-save-remote-files nil - "Don't super-save remote files.") - - (cuss super-save-exclude '(".gpg") - "Ignore these files when saving.") - - (super-save-mode +1) - - -### Auto-revert buffers to files on disk - - (global-auto-revert-mode +1) - - -### Add a timestamp to files - - (add-hook 'before-save-hook #'time-stamp) - - -### Require a final new line - - (cuss require-final-newline t) - - -### Edit files with `sudo` - - (straight-use-package 'sudo-edit) - - (with-eval-after-load 'sudo-edit - (define-key acdw/map (kbd "C-r") #'sudo-edit)) - - -#### Don’t add `/sudo:` files to `recentf`, though - -I’ve pretty much cribbed this from [recentf-remove-sudo-tramp-prefix](https://github.com/ncaq/recentf-remove-sudo-tramp-prefix/) – it’s a small enough package that I can just include it completely here. - - ;; appease the compiler - (declare-function tramp-tramp-file-p "tramp") - (declare-function tramp-dissect-file-name "tramp") - (declare-function tramp-file-name-method "tramp") - (declare-function tramp-file-name-localname "tramp") - - (defun recentf-remove-sudo-tramp-prefix (path) - "Remove sudo from PATH." - (require 'tramp) - (if (tramp-tramp-file-p path) - (let ((tx (tramp-dissect-file-name path))) - (if (string-equal "sudo" (tramp-file-name-method tx)) - (tramp-file-name-localname tx) - path)) - path) - - (defun recentf-remove-sudo-tramp-prefix-from-recentf-list () - (require 'recentf) - (setq recentf-list - (mapcar #'recentf-remove-sudo-tramp-prefix recentf-list))) - - (advice-add 'recentf-cleanup - :before #'recentf-remove-sudo-tramp-prefix-from-recentf-list)) - - -## Text editing - - -### Operate visually on lines - - (global-visual-line-mode +1) - - -### View long lines like filled lines in the beginning - - (straight-use-package 'adaptive-wrap) - - (when (fboundp 'adaptive-wrap-prefix-mode) - (defun acdw/activate-adaptive-wrap-prefix-mode () - "Toggle `visual-line-mode' and `adaptive-wrap-prefix-mode' simultaneously." - (adaptive-wrap-prefix-mode (if visual-line-mode - +1 - -1))) - (add-hook 'visual-line-mode-hook #'acdw/activate-adaptive-wrap-prefix-mode)) - - -### Stay snappy with long-lined files - - (when (fboundp 'global-so-long-mode) - (global-so-long-mode +1)) - - -### Killing & Yanking - - -#### Replace selection when typing - - (delete-selection-mode +1) - - -#### Work better with the system clipboard - - (cuss save-interprogram-paste-before-kill t - "Save existing clipboard text into the kill ring before - replacing it.") - - (cuss yank-pop-change-selection t - "Update the X selection when rotating the kill ring.") - - (cuss x-select-enable-clipboard t) - (cuss x-select-enable-primary t) - (cuss mouse-drag-copy-region t) - - -#### Don’t append the same thing twice to the kill-ring - - (cuss kill-do-not-save-duplicates t) - - -### Searching & Replacing - - -#### Replace with Anzu - - (straight-use-package 'anzu) - (require 'anzu) - - ;; show search count in the modeline - (global-anzu-mode +1) - - (cuss anzu-replace-to-string-separator " → " - "What to separate the search from the replacement.") - - (global-set-key [remap query-replace] #'anzu-query-replace) - (global-set-key [remap query-replace-regexp] #'anzu-query-replace-regexp) - - (define-key isearch-mode-map [remap isearch-query-replace] #'anzu-isearch-query-replace) - (define-key isearch-mode-map [remap isearch-query-replace-regexp] #'anzu-isearch-query-replace-regexp) - - -### Overwrite mode - - (defun acdw/overwrite-mode-change-cursor () - (setq cursor-type (if overwrite-mode t 'bar))) - - (add-hook 'overwrite-mode-hook #'acdw/overwrite-mode-change-cursor) - - (rm/whitelist-add "Ovwrt") - - -### The Mark - - (cuss set-mark-repeat-command-pop t - "Repeat `set-mark-command' with a prefix argument, without - repeatedly entering the prefix argument.") - - -### Whitespace - - (cuss whitespace-style - '(empty ;; remove blank lines at the beginning and end of buffers - indentation ;; clean up indentation - space-before-tab ;; fix mixed spaces and tabs - space-after-tab)) - - (defun acdw/whitespace-cleanup-maybe () - "Only cleanup whitespace when out-of-focus." - (acdw/when-unfocused #'whitespace-cleanup)) - - (add-hook 'before-save-hook #'acdw/whitespace-cleanup-maybe) - - -### Expand region - - (straight-use-package 'expand-region) - - (global-set-key (kbd "C-=") #'er/expand-region) - - -### Move where I mean - - (straight-use-package 'mwim) - (require 'mwim) - - (cuss mwim-beginning-of-line-function 'beginning-of-visual-line) - (cuss mwim-end-of-line-function 'end-of-visual-line) - - (global-set-key (kbd "C-a") #'mwim-beginning) - (global-set-key (kbd "C-e") #'mwim-end) - - -# Programming - - -## Prettify symbols - - (cuss prettify-symbols-unprettify-at-point 'right-edge - "Unprettify a symbol when inside it or next to it.") - - (add-hook 'prog-mode-hook #'prettify-symbols-mode) - - -## Parentheses - - -### Smart parentheses - - (straight-use-package 'smartparens) - (require 'smartparens-config) - - ;; replace show-paren - - (cuss sp-show-pair-delay 0 - "Don't delay before showing the pairs.") - (cuss sp-show-pair-from-inside t - "Highlight the enclosing pair when immediately inside.") - - (add-hook 'prog-mode-hook #'show-smartparens-mode +1) - - ;; enable strict smartparens in prog mode - (add-hook 'prog-mode-hook #'smartparens-strict-mode) - - -## Indent aggressively - - (straight-use-package 'aggressive-indent) - - (global-aggressive-indent-mode +1) - - -## Auto-fill comments only - -from [EmacsWiki](https://www.emacswiki.org/emacs/AutoFillMode). - - (defun comment-auto-fill () - (setq-local comment-auto-fill-only-comments t) - (auto-fill-mode 1)) - - (add-hook 'prog-mode-hook #'comment-auto-fill) - - -## Completion - - (straight-use-package 'company) - - (add-hook 'prog-mode-hook #'company-mode) - - (cuss company-idle-delay 0.1 - "Show company sooner.") - (cuss company-minimum-prefix-length 3 - "Don't try to complete short words.") - - (with-eval-after-load 'company - (define-key company-active-map (kbd "C-n") - (lambda () (interactive) (company-complete-common-or-cycle +1))) - (define-key company-active-map (kbd "C-p") - (lambda () (interactive) (company-complete-common-or-cycle -1)))) - - -### Give it a frame and better help - - (straight-use-package 'company-posframe) - - (with-eval-after-load 'company - (company-posframe-mode +1)) - - -### Prescient integration - - (straight-use-package 'company-prescient) - - (add-hook 'company-mode-hook #'company-prescient-mode) - - -## Language-specific packages - - -### Executable scripts - - (add-hook 'after-save-hook #'executable-make-buffer-file-executable-if-script-p) - - -### Emacs lisp - - (cuss eval-expression-print-length nil - "Don't truncate printed expressions by length.") - (cuss eval-expression-print-level nil - "Don't truncate printed expressions by level.") - - -#### Eros (Evaluation Result OverlayS) - - (straight-use-package 'eros) - - (cuss eros-eval-result-prefix ";; => " - "Prefix displayed before eros overlays.") - - (eros-mode +1) - - -#### Indent Elisp like Common Lisp - - (require 'cl-lib) - (setq lisp-indent-function 'common-lisp-indent-function) - (put 'cl-flet 'common-lisp-indent-function - (get 'flet 'common-lisp-indent-function)) - (put 'cl-labels 'common-lisp-indent-function - (get 'labels 'common-lisp-indent-function)) - (put 'if 'common-lisp-indent-function 2) - (put 'dotimes-protect 'common-lisp-indent-function - (get 'when 'common-lisp-indent-function)) - - -### Janet - - (straight-use-package 'janet-mode) - (require 'janet-mode) - - (straight-use-package '(inf-janet - :host github - :repo "velkyel/inf-janet")) - - (add-hook 'janet-mode-hook #'inf-janet-minor-mode) - - -### INI - - (straight-use-package 'ini-mode) - - (add-to-list 'auto-mode-alist - '("\\.ini\\'" . ini-mode)) - - -### PHP - -see also [this post by Fermin M](https://sasanidas.gitlab.io/f-site/php-development/), it looks really useful. - - (straight-use-package 'php-mode) - - -# Writing - - -## Visual fill column - - -### Fix scrolling in margins - -This has to be done *before* loading the package. It's included in `visual-fill-column`, too, but for some reason isn't loaded there. - - (dolist (margin '(right-margin left-margin)) - (dolist (button '(mouse-1 mouse-2 mouse-3)) - (global-set-key (vector margin button) - (global-key-binding (vector button))))) - - (mouse-wheel-mode +1) - - (when (bound-and-true-p mouse-wheel-mode) - (dolist (margin '(right-margin left-margin)) - (dolist (event '(mouse-wheel-down-event - mouse-wheel-up-event - wheel-down - wheel-up - mouse-4 - mouse-5)) - (global-set-key (vector margin event) #'mwheel-scroll)))) - - -### Load the package - - (straight-use-package 'visual-fill-column) - - (cuss visual-fill-column-center-text nil - "Whether to center the text in the frame.") - - (cuss fill-column 84 - "Width of fill-column, and thus, visual-fill-column.") - - (advice-add 'text-scale-adjust - :after #'visual-fill-column-adjust) - - (global-visual-fill-column-mode +1) - - -## Typographical niceties - - -### Variable pitch in text-modes - - (add-hook 'text-mode-hook #'variable-pitch-mode) - - -### Typo mode - - (straight-use-package 'typo) - - (add-hook 'text-mode-hook #'typo-mode) - - ;; Disable `typo-mode' when inside an Org source block - (with-eval-after-load 'typo - (add-to-list 'typo-disable-electricity-functions - #'org-in-src-block-p)) - - -### Show `^L` as a horizontal line - - (straight-use-package 'form-feed) - (global-form-feed-mode +1) - - -## Word count - - (straight-use-package 'wc-mode) - - (add-hook 'text-mode-hook #'wc-mode) - - (rm/whitelist-add "WC") - - -# Applications - - -## Web browsing - - (cuss browse-url-browser-function 'browse-url-firefox) - (cuss browse-url-new-window-flag t - "Always open a new browser window.") - - ;;(cuss browse-url-generic-program "firefox") - (cuss browse-url-firefox-new-window-is-tab t - "Or a new tab, in Firefox.") - - ;; we need to add Firefox to `exec-path' on Windows - (when-at :work - (add-to-list 'exec-path "c:/Program Files/Mozilla Firefox")) - - -## Dired - - -### Basic customization - - (defun acdw/setup-dired-mode () - (hl-line-mode) - (dired-hide-details-mode)) - - ;; highlight the current line in dired. - (add-hook 'dired-mode-hook #'acdw/setup-dired-mode) - - (cuss dired-recursive-copies 'always - "Always recursively copy.") - - (cuss dired-recursive-deletes 'always - "Always recursively delete.") - - (cuss delete-by-moving-to-trash t) - - (cuss dired-listing-switches "-alh" - "Show (A)lmost all items, - (l)isted out, with (h)uman-readable sizes.") - - -### Expand subtrees - - (straight-use-package 'dired-subtree) - - (with-eval-after-load 'dired - (define-key dired-mode-map "i" #'dired-subtree-toggle)) - - -### Collapse singleton directories - - (straight-use-package 'dired-collapse) - - (add-hook 'dired-mode-hook #'dired-collapse-mode) - - -### Kill dired buffers - -from [munen](https://github.com/munen/emacs.d/). - - (defun kill-dired-buffers () - "Kill all open dired buffers." - (interactive) - (mapc (lambda (buffer) - (when (eq 'dired-mode (buffer-local-value 'major-mode buffer)) - (kill-buffer buffer))) - (buffer-list))) - - -## Org mode - -I’ve put org mode under Applications, as opposed to Writing, because it’s more generally-applicable than that. - - -### Basics - - (straight-use-package 'org) - - (with-eval-after-load 'org - (require 'org-tempo) - (require 'ox-md) - (define-key org-mode-map (kbd "M-n") #'outline-next-visible-heading) - (define-key org-mode-map (kbd "M-p") #'outline-previous-visible-heading)) - - (cuss org-hide-emphasis-markers t) - (cuss org-fontify-done-headline t) - (cuss org-fontify-whole-heading-line t) - (cuss org-fontify-quote-and-verse-blocks t) - (cuss org-pretty-entities t) - (cuss org-src-tab-acts-natively t) - (cuss org-src-fontify-natively t) - (cuss org-src-window-setup 'current-window) - (cuss org-confirm-babel-evaluate nil) - (cuss org-directory "~/Org") - (cuss org-ellipsis "…") - (cuss org-catch-invisible-edits 'show) - (cuss org-special-ctrl-a/e t) - (cuss org-special-ctrl-k t) - - (cuss org-export-headline-levels 8 - "Maximum level of headlines to export /as/ a headline.") - - -#### TODO configure `mwim` to work with Org mode - - -#### Tags - - (cuss org-tags-column 0 - "Show tags directly after the headline. - This is the best-looking option with variable-pitch fonts.") - - (cussface - '(org-tag - ((t - (:height 0.8 :weight normal :slant italic :foreground "grey40" :inherit - (variable-pitch)))))) - - -##### Align all tags in the buffer on changes - -from [mpereira](https://github.com/mpereira/.emacs.d#align-all-tags-in-the-buffer-on-tag-changes). - - (defun acdw/org-align-all-tags () - "Align all org tags in the buffer." - (interactive) - (when (eq major-mode 'org-mode) - (org-align-tags t))) - - (add-hook 'org-after-tags-change-hook #'acdw/org-align-all-tags) - - -#### Source blocks - - (with-eval-after-load 'org-faces - (set-face-attribute 'org-block-begin-line nil - :height 0.85)) - - -#### Prettify - - (defun acdw/org-mode-prettify () - "Prettify `org-mode'." - (dolist (cell '(("[ ]" . ?☐) ("[X]" . ?☑) ("[-]" . ?◐) - ("#+BEGIN_SRC" . ?✎) ("#+begin_src" . ?✎) - ("#+END_SRC" . ?■) ("#+end_src" . ?■))) - (add-to-list 'prettify-symbols-alist cell :append)) - (prettify-symbols-mode +1)) - - (add-hook 'org-mode-hook #'acdw/org-mode-prettify) - - -### General - - -#### [Org Return: DWIM](https://github.com/alphapapa/unpackaged.el#org-return-dwim) - - (defun unpackaged/org-element-descendant-of (type element) - "Return non-nil if ELEMENT is a descendant of TYPE. - TYPE should be an element type, like `item' or `paragraph'. - ELEMENT should be a list like that returned by `org-element-context'." - ;; MAYBE: Use `org-element-lineage'. - (when-let* ((parent (org-element-property :parent element))) - (or (eq type (car parent)) - (unpackaged/org-element-descendant-of type parent)))) - - ;;;###autoload - (defun unpackaged/org-return-dwim (&optional default) - "A helpful replacement for `org-return'. With prefix, call `org-return'. - - On headings, move point to position after entry content. In - lists, insert a new item or end the list, with checkbox if - appropriate. In tables, insert a new row or end the table." - ;; Inspired by John Kitchin: http://kitchingroup.cheme.cmu.edu/blog/2017/04/09/A-better-return-in-org-mode/ - (interactive "P") - (if default - (org-return) - (cond - ;; Act depending on context around point. - - ;; NOTE: I prefer RET to not follow links, but by uncommenting this block, links will be - ;; followed. - - ;; ((eq 'link (car (org-element-context))) - ;; ;; Link: Open it. - ;; (org-open-at-point-global)) - - ((org-at-heading-p) - ;; Heading: Move to position after entry content. - ;; NOTE: This is probably the most interesting feature of this function. - (let ((heading-start (org-entry-beginning-position))) - (goto-char (org-entry-end-position)) - (cond ((and (org-at-heading-p) - (= heading-start (org-entry-beginning-position))) - ;; Entry ends on its heading; add newline after - (end-of-line) - (insert "\n\n")) - (t - ;; Entry ends after its heading; back up - (forward-line -1) - (end-of-line) - (when (org-at-heading-p) - ;; At the same heading - (forward-line) - (insert "\n") - (forward-line -1)) - ;; FIXME: looking-back is supposed to be called with more arguments. - (while (not (looking-back (rx (repeat 3 (seq (optional blank) "\n"))) nil)) - (insert "\n")) - (forward-line -1))))) - - ((org-at-item-checkbox-p) - ;; Checkbox: Insert new item with checkbox. - (org-insert-todo-heading nil)) - - ((org-in-item-p) - ;; Plain list. Yes, this gets a little complicated... - (let ((context (org-element-context))) - (if (or (eq 'plain-list (car context)) ; First item in list - (and (eq 'item (car context)) - (not (eq (org-element-property :contents-begin context) - (org-element-property :contents-end context)))) - (unpackaged/org-element-descendant-of 'item context)) ; Element in list item, e.g. a link - ;; Non-empty item: Add new item. - (org-insert-item) - ;; Empty item: Close the list. - ;; TODO: Do this with org functions rather than operating on the text. Can't seem to find the right function. - (delete-region (line-beginning-position) (line-end-position)) - (insert "\n")))) - - ((when (fboundp 'org-inlinetask-in-task-p) - (org-inlinetask-in-task-p)) - ;; Inline task: Don't insert a new heading. - (org-return)) - - ((org-at-table-p) - (cond ((save-excursion - (beginning-of-line) - ;; See `org-table-next-field'. - (cl-loop with end = (line-end-position) - for cell = (org-element-table-cell-parser) - always (equal (org-element-property :contents-begin cell) - (org-element-property :contents-end cell)) - while (re-search-forward "|" end t))) - ;; Empty row: end the table. - (delete-region (line-beginning-position) (line-end-position)) - (org-return)) - (t - ;; Non-empty row: call `org-return'. - (org-return)))) - (t - ;; All other cases: call `org-return'. - (org-return))))) - - (with-eval-after-load 'org - (define-key org-mode-map (kbd "RET") #'unpackaged/org-return-dwim)) - - -#### Insert blank lines around headers - -from [unpackaged.el](https://github.com/alphapapa/unpackaged.el#ensure-blank-lines-between-headings-and-before-contents). - - ;;;###autoload - (defun unpackaged/org-fix-blank-lines (&optional prefix) - "Ensure that blank lines exist between headings and between headings and their contents. - With prefix, operate on whole buffer. Ensures that blank lines - exist after each headings's drawers." - (interactive "P") - (org-map-entries (lambda () - (org-with-wide-buffer - ;; `org-map-entries' narrows the buffer, which prevents us - ;; from seeing newlines before the current heading, so we - ;; do this part widened. - (while (not (looking-back "\n\n" nil)) - ;; Insert blank lines before heading. - (insert "\n"))) - (let ((end (org-entry-end-position))) - ;; Insert blank lines before entry content - (forward-line) - (while (and (org-at-planning-p) - (< (point) (point-max))) - ;; Skip planning lines - (forward-line)) - (while (re-search-forward org-drawer-regexp end t) - ;; Skip drawers. You might think that `org-at-drawer-p' - ;; would suffice, but for some reason it doesn't work - ;; correctly when operating on hidden text. This - ;; works, taken from `org-agenda-get-some-entry-text'. - (re-search-forward "^[ \t]*:END:.*\n?" end t) - (goto-char (match-end 0))) - (unless (or (= (point) (point-max)) - (org-at-heading-p) - (looking-at-p "\n")) - (insert "\n")))) - t (if prefix - nil - 'tree))) - - -##### Add a before-save-hook - - (defun cribbed/org-mode-fix-blank-lines () - (when (eq major-mode 'org-mode) - (let ((current-prefix-arg 4)) ; Emulate C-u - (call-interactively 'unpackaged/org-fix-blank-lines)))) - - (add-hook 'before-save-hook #'cribbed/org-mode-fix-blank-lines) - - -### Org Templates (`org-tempo`) - - (with-eval-after-load 'org - (add-to-list 'org-structure-template-alist - '("el" . "src emacs-lisp"))) - - -### Org Agenda - - (cuss org-agenda-files - (let ((list)) - (dolist (file '(;; add more files to this list - "home.org" - "work.org") - list) - (push (expand-file-name file org-directory) list)))) - - (define-key acdw/map (kbd "C-a") #'org-agenda) - - (cuss org-agenda-span 5 - "Show today + N days.") - - (cuss org-todo-keywords - '((sequence "RECUR(r)" "TODO(t)" "|" "DONE(d)") - (sequence "APPT(a)") - (sequence "|" "CANCELLED(c)"))) - - (cuss org-agenda-skip-scheduled-if-done t) - (cuss org-agenda-skip-deadline-if-done t) - (cuss org-deadline-warning-days 0 - "Don't warn of an impending deadline.") - - (cuss org-log-into-drawer "LOGBOOK" - "Log state changes into the LOGBOOK drawer, instead of after - the headline.") - (cuss org-log-done t - "Save CLOSED timestamp when done.") - - -### Capture - - -#### Templates - - (cuss org-default-notes-file (expand-file-name "inbox.org" - org-directory) - "Put unfiled notes in ~/Org/inbox.org.") - - - (cuss org-capture-templates - '(("w" "Work Todo") ;;; Work stuff - ("ws" "Small Business" entry - (file+headline "work.org" "Small Business") - "* TODO %? - :PROPERTIES: - :Via: - :Note: - :END: - :LOGBOOK: - - State \"TODO\" from \"\" %U - :END:" :empty-lines 1) - ("wc" "Career Center" entry - (file+headline "work.org" "Career Center") - "* TODO %? - :PROPERTIES: - :Via: - :Note: - :END: - :LOGBOOK: - - State \"TODO\" from \"\" %U - :END:" :empty-lines 1) - ("wg" "General" entry - (file+headline "work.org" "General") - "* TODO %? - :PROPERTIES: - :Via: - :Note: - :END: - :LOGBOOK: - - State \"TODO\" from \"\" %U - :END:" :empty-lines 1) - - ;;; Regular To-Dos - ("t" "Todo") - ("tt" "Todo" entry - (file+headline "home.org" "TODOs") - "* TODO %? - :PROPERTIES: - :Via: - :Note: - :END: - :LOGBOOK: - - State \"TODO\" from \"\" %U - :END:" :empty-lines 1) - - ("td" "Todo with deadline" entry - (file+headline "home.org" "TODOs") - "* TODO %? - DEADLINE: %^t - :PROPERTIES: - :Via: - :Note: - :END: - :LOGBOOK: - - State \"TODO\" from \"\" %U - :END:" :empty-lines 1) - - - ("g" "Gift idea" entry - (file+headline "home.org" "Gift ideas") - "* %^{Who?} - * %^{What?}" :empty-lines 1) - - ("d" "Diary entry" entry - (file+olp+datetree "diary.org") - "* %? - Entered on %U - - " :empty-lines 1))) - - -#### Keybindings - - (require 'org-capture) - - (with-eval-after-load 'org-capture - (define-key acdw/map (kbd "C-c") #'org-capture)) - - -### Include Org links in source code - - (straight-use-package '(org-link-minor-mode - :host github - :repo "seanohalpin/org-link-minor-mode")) - - ;; enable in elisp buffers - (add-hook 'emacs-lisp-mode-hook #'org-link-minor-mode) - - -## Git - - (straight-use-package 'magit) - - (define-key acdw/map "g" #'magit-status) - - -### Git file modes - - (dolist (feat '(gitattributes-mode - gitconfig-mode - gitignore-mode)) - (straight-use-package feat) - (require feat)) - - -## Beancount mode - - (straight-use-package '(beancount-mode - :host github - :repo "beancount/beancount-mode")) - (require 'beancount) - - (add-to-list 'auto-mode-alist '("\\.beancount\\'" . beancount-mode)) - - (defun acdw/disable-aggressive-indent () - "Turn `aggressive-indent-mode' off for a buffer." - (aggressive-indent-mode -1)) - - (add-hook 'beancount-mode-hook #'outline-minor-mode) - (add-hook 'beancount-mode-hook #'acdw/disable-aggressive-indent) - - (define-key beancount-mode-map (kbd "M-n") #'outline-next-visible-heading) - (define-key beancount-mode-map (kbd "M-p") #'outline-previous-visible-heading) - - -### Company integration with company-ledger - - (straight-use-package 'company-ledger) - - (with-eval-after-load 'company - (add-to-list 'company-backends 'company-ledger)) - - -## PDF Tools - -I’m only enabling this at home for now, since it requires building stuff. - - (defun acdw/disable-visual-fill-column-mode () - "Disable `visual-fill-column-mode'." - (visual-fill-column-mode -1)) - - (eval-when-compile - (when-at :home - (straight-use-package 'pdf-tools) - (pdf-loader-install) - - (add-hook 'pdf-view-mode-hook #'acdw/disable-visual-fill-column-mode))) - - -## E-book tools - - (straight-use-package 'nov) - - (add-to-list 'auto-mode-alist '("\\.epub\\'" . nov-mode)) - - (cuss nov-text-width t - "Disable text filling -- `visual-fill-column-mode' takes care - of that.") - - (defun acdw/setup-nov-mode () - (visual-fill-column-mode +1) - (setq visual-fill-column-center-text t) - (text-scale-increase +1)) - - (add-hook 'nov-mode-hook #'acdw/setup-nov-mode) - - -## Email - - (when-at (:home (executable-find "mu")) - (add-to-list 'load-path - "/usr/share/emacs/site-lisp/mu4e") - (require 'mu4e) - - (cuss mail-user-agent 'mu4e-user-agent) - - (cuss mu4e-headers-skip-duplicates t) - (cuss mu4e-view-show-images t) - (cuss mu4e-view-show-addresses t) - (cuss mu4e-compose-format-flowed t) - (cuss mu4e-change-filenames-when-moving t) - (cuss mu4e-attachments-dir "~/Downloads") - - (cuss mu4e-maildir "~/.mail/fastmail") - (cuss mu4e-refile-folder "/Archive") - (cuss mu4e-sent-folder "/Sent") - (cuss mu4e-drafts-folder "/Drafts") - (cuss mu4e-trash-folder "/Trash") - - (fset 'my-move-to-trash "mTrash") - (define-key mu4e-headers-mode-map (kbd "d") 'my-move-to-trash) - (define-key mu4e-view-mode-map (kbd "d") 'my-move-to-trash) - - (cuss message-send-mail-function 'smtpmail-send-it) - (cuss smtpmail-default-smtp-server "smtp.fastmail.com") - (cuss smtpmail-smtp-server "smtp.fastmail.com") - (cuss smtpmail-stream-type 'ssl) - (cuss smtpmail-smtp-service 465) - (cuss smtpmail-local-domain "acdw.net") - (cuss mu4e-compose-signature - "Best,\nCase\n") - - (cuss mu4e-completing-read-function 'completing-read) - (cuss message-kill-buffer-on-exit t) - (cuss mu4e-confirm-quit nil) - - (cuss mu4e-bookmarks - '((:name "Unread" - :query - "flag:unread AND NOT flag:trashed AND NOT maildir:/Spam" - :key ?u) - (:name "Today" - :query - "date:today..now and not flag:trashed and not maildir:/Spam" - :key ?t) - (:name "This week" - :query - "date:7d..now and not maildir:/Spam and not flag:trashed" - :hide-unread t - :key ?w))) - - (cuss mu4e-headers-fields - '((:human-date . 12) - (:flags . 6) - (:mailing-list . 10) - (:from-or-to . 22) - (:thread-subject))) - - (cuss mu4e-maildir-shortcuts - `(("/INBOX" . ?i) - (,mu4e-refile-folder . ?a) - (,mu4e-sent-folder . ?s) - (,mu4e-drafts-folder . ?d) - (,mu4e-trash-folder . ?t))) - - (cuss mu4e-get-mail-command (cond ((executable-find "mbsync") - "mbsync -a")) - "The command to update mail with.") - (cuss mu4e-update-interval 300 - "Update automatically every 5 minutes.") - - (defun acdw/setup-mu4e-headers-mode () - (visual-line-mode -1)) - (add-hook 'mu4e-headers-mode #'acdw/setup-mu4e-headers-mode) - - (defun acdw/setup-mu4e-view-mode () - (setq visual-fill-column-center-text t) - (visual-fill-column-mode +1)) - (add-hook 'mu4e-view-mode-hook #'acdw/setup-mu4e-view-mode) - (add-hook 'mu4e-compose-mode-hook #'acdw/setup-mu4e-view-mode) - - (declare-function mu4e "mu4e") - (mu4e +1)) - - -### Add a keybinding - - (defun acdw/mu4e-or-warn () - "If `mu4e' is around, run it, or tell the user it isn't." - (interactive) - (if (featurep 'mu4e) - (mu4e) - (warn "Mu4e isn't available :/."))) - - (define-key acdw/map "m" #'acdw/mu4e-or-warn) - - -## Smolweb - - -### A common function to make a cohesive smolweb experience - - (defun acdw/setup-smolweb () - "Configure emacs to view the smolweb." - (setq visual-fill-column-center-text t) - (visual-fill-column-mode +1) - (visual-line-mode +1) - (variable-pitch-mode -1) - (text-scale-increase +1)) - - -### Elpher - - (straight-use-package '(elpher - :repo "git://thelambdalab.xyz/elpher.git")) - - (with-eval-after-load 'no-littering - (cuss elpher-certificate-directory - (no-littering-expand-var-file-name "elpher-certificates/"))) - - (cuss elpher-ipv4-always t) - - (cussface '(elpher-gemini-heading1 - ((t (:inherit (modus-theme-heading-1 variable-pitch)))))) - (cussface '(elpher-gemini-heading2 - ((t (:inherit (modus-theme-heading-2 variable-pitch)))))) - (cussface '(elpher-gemini-heading3 - ((t (:inherit (modus-theme-heading-3 variable-pitch)))))) - - (defun elpher:eww-browse-url (original url &optional new-window) - "Handle gemini/gopher links with eww." - (cond ((string-match-p "\\`\\(gemini\\|gopher\\)://" url) - (require 'elpher) - (elpher-go url)) - (t (funcall original url new-window)))) - (advice-add 'eww-browse-url :around 'elpher:eww-browse-url) - - (with-eval-after-load 'elpher - (define-key elpher-mode-map "n" #'elpher-next-link) - (define-key elpher-mode-map "p" #'elpher-prev-link) - (define-key elpher-mode-map "o" #'elpher-follow-current-link) - (define-key elpher-mode-map "G" #'elpher-go-current)) - - (add-hook 'elpher-mode-hook #'acdw/setup-smolweb) - - (autoload 'elpher-bookmarks "elpher") - (define-key acdw/map "e" #'elpher-bookmarks) - - -### Gemini-mode - - (straight-use-package '(gemini-mode - :repo "https://git.carcosa.net/jmcbray/gemini.el.git")) - - (add-to-list 'auto-mode-alist - '("\\.\\(gemini\\|gmi\\)\\'" . gemini-mode)) - - (cussface '(gemini-heading-face-1 - ((t (:inherit (elpher-gemini-heading1)))))) - (cussface '(gemini-heading-face2 - ((t (:inherit (elpher-gemini-heading2)))))) - (cussface '(gemini-heading-face3 - ((t (:inherit (elpher-gemini-heading3)))))) - - (add-hook 'gemini-mode-hook #'acdw/setup-smolweb) - - -### Gemini-write - - (straight-use-package '(gemini-write - :repo "https://tildegit.org/acdw/gemini-write.git")) - (require 'gemini-write) - - -## RSS - - -### elfeed - - (straight-use-package 'elfeed) - (require 'elfeed) - (define-key acdw/map "w" 'elfeed) - - (cuss elfeed-use-curl (executable-find "curl")) - (cuss elfeed-curl-extra-arguments '("--insecure") - "Extra arguments for curl.") - (elfeed-set-timeout (* 60 3)) - - (defun acdw/setup-elfeed-show () - (setq visual-fill-column-center-text t) - (visual-fill-column-mode +1)) - - (add-hook 'elfeed-show-mode-hook #'acdw/setup-elfeed-show) - - -### elfeed-protocol - - (straight-use-package 'elfeed-protocol) - (require 'elfeed-protocol) - - (cuss elfeed-protocol-ttrss-maxsize 200) - - (cuss elfeed-feeds (list - (list "ttrss+https://acdw@rss.tildeverse.org" - :use-authinfo t))) - - ;; Uncomment this line if elfeed is giving problems. - ;; (setq elfeed-log-level 'debug) - - (elfeed-protocol-enable) - - (defun acdw/update-elfeed-protocol-feeds () - "Wrap various (non-working) protocol updaters. - I could probably do this in a much more ... better way." - (interactive) - (elfeed-protocol-ttrss-reinit "https://rss.tildeverse.org")) - - (with-eval-after-load 'elfeed - (define-key elfeed-search-mode-map "G" #'acdw/update-elfeed-protocol-feeds)) - - -# System integration - - -## Linux - - -### Exec path from shell - - (eval-when-compile - (when-at :home - (straight-use-package 'exec-path-from-shell) - (defvar acdw/exec-path-from-shell-initialized nil - "Stores whether we've initialized or not.") - (unless acdw/exec-path-from-shell-initialized - (exec-path-from-shell-initialize) - (setq acdw/exec-path-from-shell-initialized (current-time))))) - - -# Areas for further research - -- [Use a minor-mode for keybindings](https://github.com/larstvei/dot-emacs#key-bindings) - - -# Appendices - - -## Emacs' files - - -### init.el - - ;; init.el -*- lexical-binding: t -*- - - (setq load-prefer-newer t) - - (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))) - - -### early-init.el - - ;; early-init.el -*- no-byte-compile: t; -*- - - ;; I use `straight.el' instead of `package.el'. - (setq package-enable-at-startup nil) - - ;; Don't resize the frame when loading fonts - (setq frame-inhibit-implied-resize t) - ;; Resize frame by pixels - (setq frame-resize-pixelwise t) - - -## Ease tangling and loading of Emacs' init - - (defun refresh-emacs (&optional disable-load) - "Tangle `config.org', then byte-compile the resulting files. - Then, load the byte-compilations unless passed with a prefix argument." - (interactive "P") - (let ((config (expand-file-name "config.org" user-emacs-directory))) - (save-mark-and-excursion - (with-current-buffer (find-file-noselect config) - (let ((prog-mode-hook nil)) - ;; generate the readme - (when (file-newer-than-file-p config (expand-file-name - "README.md" - user-emacs-directory)) - (message "%s" "Exporting README.md...") - (require 'ox-md) - (with-demoted-errors "Problem exporting README.md: %S" - (org-md-export-to-markdown))) - ;; tangle config.org - (when (file-newer-than-file-p config (expand-file-name - "config.el" - user-emacs-directory)) - (message "%s" "Tangling config.org...") - (add-to-list 'load-path (expand-file-name "straight/build/org/" - user-emacs-directory)) - (require 'org) - (let ((inits (org-babel-tangle))) - ;; byte-compile resulting files - (message "%s" "Byte-compiling...") - (dolist (f inits) - (when (string-match "\\.el\\'" f) - (byte-compile-file f (not disable-load))))))))))) - - -## Ancillary scripts - - -### emacsdc - -Here's a wrapper script that'll start `emacs --daemon` if there isn't -one, and then launch `emacsclient` with the arguments. I'd recommend -installing with either `ln -s bin/emacsdc $HOME/.local/bin/`, or -adding `$HOME/.local/bin` to your `$PATH`. - - if ! emacsclient -nc "$@" 2>/dev/null; then - emacs --daemon - emacsclient -nc "$@" - fi - - -### Emacs.cmd - -Here's a wrapper script that'll run Emacs on Windows, with a -custom `$HOME`. I have mine setup like this: Emacs is downloaded -from [the GNU mirror](https://mirrors.tripadvisor.com/gnu/emacs/windows/emacs-27/emacs-27.1-x86_64.zip) and unzipped to `~/Downloads/emacs/`. For -some reason, Emacs by default sets `$HOME` to `%APPDATA%`, which -doesn’t make a lot of sense to me. I change it to -`%USERPROFILE%\Downloads\home`, and then run Emacs with the -supplied arguments. - -As far as creating a shortcut to the Desktop, you’ll have to do -that yourself – *apparently* Windows doesn’t have a built-in -shortcut-creating software >:(. - - REM Set variables - set HOME=%USERPROFILE%\Downloads\home - set EMACS=%USERPROFILE%\Downloads\emacs\bin\runemacs.exe - - REM Change the directory - chdir %HOME% - - REM Run "Quick Mode" - REM "%EMACS%" -Q %* - - REM Regular - "%EMACS%" %* - - -### emacsclient.desktop - -from [taingram](https://www.taingram.org/blog/emacs-client.html). - - [Desktop Entry] - Name=Emacs Client - GenericName=Text Editor - Comment=Edit text - MimeType=text/english;text/plain;text/x-makefile;text/x-c++hdr;text/x-c++src;text/x-chdr;text/x-csrc;text/x-java;text/x-moc;text/x-pascal;text/x-tcl;text/x-tex;application/x-shellscript;text/x-c;text/x-c++; - Exec=emacsclient -c %f - Icon=emacs - Type=Application - Terminal=false - Categories=Utility;TextEditor; - - -## License - -Copyright © 2020 Case Duckworth - -This work is free. You can redistribute it and/or modify it under the -terms of the Do What the Fuck You Want To Public License, Version 2, -as published by Sam Hocevar. See the `LICENSE` file, tangled from the -following source block, for details. - - DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE - - Version 2, December 2004 - - Copyright (C) 2004 Sam Hocevar - - Everyone is permitted to copy and distribute verbatim or modified copies of - this license document, and changing it is allowed as long as the name is changed. - - DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE - - TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION - - 0. You just DO WHAT THE FUCK YOU WANT TO. - - -### Note on the license - -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!** - - -## TODO Local variables - -One day, I’m going to add a Local Variables block to the end of this file, which will add an `after-save-hook` to auto-tangle the file after saving. But first I need to research how best to do that asynchronously. So. - -- cgit 1.4.1-21-gabe81