Emacs Init File!
This is my emacs configuration. I keep it in org-mode and automatically tangle
it to ~/.config/emacs/init.el on save (see Local vars). This makes it easy to
document and organize.
Most of the functions and variables I define myself are prefixed with d/, just
so I can keep track of them.
If you have a question about any of the built-in variables, functions, modes,
etc. that you see here, M-x describe-symbol (bound to C-h o in vanilla Emacs)
might be a good place to start.
Initial Setup
Settings
Lexical binding is a variable scope matter in Emacs lisp. I won't explain it here but I've linked the manual page for it. It's a good idea to turn this on. This may be the default in a future version of Emacs.
Your eyes do not deceive you; this is indeed a comment. It's using special
emacs syntax (-*- variable: value -*-) to set file-local variables.
;;; -*- lexical-binding: t -*-
I like to keep a local "lisp" directory in my emacs config for slightly more
involved lisp projects that I don't want cluttering my init and may not want to
officially package. Emacs 31 introduces the user-lisp directory that's
automatically on the load-path, but that isn't released yet at the time of
writing.
(add-to-list 'load-path (expand-file-name "lisp" user-emacs-directory))
Some libraries I find useful within my configuration.
(require 'seq) (require 'subr-x)
Utils
This is just a convenience macro around with-eval-after-load. Often I want do things after a package loads (and not in the same form as use-package, mostly for organizational reasons). I don't want errors to totally stop my startup.
(defmacro d/after (feature &rest body) "Load BODY after FEATURE, catching errors and displaying as warnings." (declare (indent defun)) `(with-eval-after-load ,feature (condition-case-unless-debug err (progn ,@body) (error (display-warning 'init (format "%s eval-after-load: %s " (symbol-name ,feature) (error-message-string err)) :error)))))
Package management
elpaca
Elpaca is a source-based, parallel package manager. It's a kind of successor to straight.el. It's useful if you do a lot of hacking on elisp packages, or need to pin specific versions of packages. The parallelism is nice, too.
At some point, I may switch back to the built-in package.el with the :vc
keyword to use-package in special cases. Or maybe not, we'll see.
(defvar elpaca-installer-version 0.12) (defvar elpaca-directory (expand-file-name "elpaca/" user-emacs-directory)) (defvar elpaca-builds-directory (expand-file-name "builds/" elpaca-directory)) (defvar elpaca-sources-directory (expand-file-name "sources/" elpaca-directory)) (defvar elpaca-order '(elpaca :repo "https://github.com/progfolio/elpaca.git" :ref nil :depth 1 :inherit ignore :files (:defaults "elpaca-test.el" (:exclude "extensions")) :build (:not elpaca-activate))) (let* ((repo (expand-file-name "elpaca/" elpaca-sources-directory)) (build (expand-file-name "elpaca/" elpaca-builds-directory)) (order (cdr elpaca-order)) (default-directory repo)) (add-to-list 'load-path (if (file-exists-p build) build repo)) (unless (file-exists-p repo) (make-directory repo t) (when (<= emacs-major-version 28) (require 'subr-x)) (condition-case-unless-debug err (if-let* ((buffer (pop-to-buffer-same-window "*elpaca-bootstrap*")) ((zerop (apply #'call-process `("git" nil ,buffer t "clone" ,@(when-let* ((depth (plist-get order :depth))) (list (format "--depth=%d" depth) "--no-single-branch")) ,(plist-get order :repo) ,repo)))) ((zerop (call-process "git" nil buffer t "checkout" (or (plist-get order :ref) "--")))) (emacs (concat invocation-directory invocation-name)) ((zerop (call-process emacs nil buffer nil "-Q" "-L" "." "--batch" "--eval" "(byte-recompile-directory \".\" 0 'force)"))) ((require 'elpaca)) ((elpaca-generate-autoloads "elpaca" repo))) (progn (message "%s" (buffer-string)) (kill-buffer buffer)) (error "%s" (with-current-buffer buffer (buffer-string)))) ((error) (warn "%s" err) (delete-directory repo 'recursive)))) (unless (require 'elpaca-autoloads nil t) (require 'elpaca) (elpaca-generate-autoloads "elpaca" repo) (let ((load-source-file-function nil)) (load "./elpaca-autoloads")))) (add-hook 'after-init-hook #'elpaca-process-queues) (elpaca `(,@elpaca-order)) ;; Install use-package support (elpaca elpaca-use-package ;; Enable :ensure use-package keyword. (elpaca-use-package-mode)) ;; Block until current queue processed. (elpaca-wait)
use-package
An excellent utility for managing packages and package configuration in a neat and organized way, with advanced support for deferring, pre/post-loading configuration, time reporting, and more.
You can use the same init file across computers without keeping track of what's installed and it will ensure that any missing packages are installed. You can also conditionalize package load by system (or anything else). It's pretty neat.
Read the manual linked above!
(setq use-package-minimum-reported-time .001 use-package-verbose t use-package-always-defer t use-package-always-ensure t use-package-compute-statistics t)
Packages
compat
Compat is a forwards-compatibility library. I have this here because the vertico stack recently broke on me and this fixed it. Honestly, I'm not exactly sure what that was about.
(use-package compat :ensure (:wait t) :demand t :config (require 'compat-31)) (elpaca-wait)
no-littering
Usually, a bunch of files are kept in your .emacs.d folder by both built-in
emacs features and external packages. This package sets up a convention to
store everything in either .emacs.d/var for data, or .emacs.d/etc for
configuration.
(use-package no-littering :demand t :config (no-littering-theme-backups) (setq custom-file (no-littering-expand-etc-file-name "custom.el")) (add-hook 'elpaca-after-init-hook (lambda () (load custom-file 'noerror))))
hydra
Hydra lets you create small, transient keymaps with a persistent help menu. They tend to quit when you pressa key not in the hydra. I don't use this as much as I used to, but I still have some hydras defined in my init.
(use-package hydra :config (setq hydra-default-hint nil))
savehist
savehist is a built-in mode to save minibuffer (and other) histories between emacs sessions.
(use-package savehist :ensure nil :defer 5 :config (savehist-mode 1) (add-to-list 'savehist-additional-variables 'kill-ring))
saveplace
saveplace is a built-in mode to save and re-visit your last position in files after you close them (or emacs).
(use-package saveplace :ensure nil :defer 5 :config (save-place-mode 1))
midnight
midnight is a bulit-in mode to clean up saved buffers that haven't been displayed recently on some regular interval, by default a day.
(use-package midnight :ensure nil :defer 10 :custom (clean-buffer-list-delay-general 1) (clean-buffer-list-kill-regexps '("\\`\\*Man " "\\`\\*helpful ")) :config (midnight-mode))
Core
Settings
Don't show me the startup screen, just get me to the editor!
(setq inhibit-startup-screen t inhibit-startup-echo-area-message t)
Start the scratch buffer in fundamental-mode with no message. This is to avoid
emacs-lisp-mode and prog-mode hooks from slowing down startup. We get back our
old scratch buffer eventually with persistent-scratch anyway.
(setq initial-scratch-message "" initial-major-mode 'fundamental-mode)
Use TAB for indentation, then completion.
(setq tab-always-indent 'complete)
Stop beeping!
(setq ring-bell-function #'ignore)
Copy pasting settings:
- Save current external clipboard item to kill ring
- Enable the primary selection; this is… not so relevant to me these days.
(setq save-interprogram-paste-before-kill t select-enable-primary t)
Minibuffer settings:
- Use the minibuffer… from the minibuffer
- Resize minibuffer windows to fit content
- Make the minibuffer prompt untouchable
- Indicate minibuffer depth lest we lose our minds
(setq enable-recursive-minibuffers t resize-mini-windows t minibuffer-prompt-properties '(read-only t intangible t cursor-intangible t face minibuffer-prompt)) (add-hook 'minibuffer-setup-hook #'cursor-intangible-mode) (minibuffer-depth-indicate-mode t)
More messages!
(setf message-log-max 10000)
A couple other things:
- Don't put native comp warnings front and center; it can be noisy.
- Don't disable any commands; I generally know what I'm doing.
- When killing emacs, just kill it without asking to kill processes.
- Disable
view-hello-file, because I keep accidentally pressingC-h hsomehow. This is silly.
(setf native-comp-async-report-warnings-errors 'silent disabled-command-function nil confirm-kill-processes nil) (defalias #'view-hello-file #'ignore)
Packages
These are packages that I consider absolutely essential to my emacs workflow, or that enhance emacs at a deeper level than any regular mode.
darktooth-theme
…because MY EYES! Some people argue dark themes are worse for your vission or less legible. This may be true. Oh well.
This is my own fork of the most excellent emacsfodder/emacs-theme-darktooth with many of my own tweaks.
(use-package darktooth-theme :demand t :ensure (:host github :repo "emacsfodder/emacs-theme-darktooth" :remotes ("fork" :host github :repo "dieggsy/emacs-theme-darktooth")) :custom (custom-safe-themes t) :config (load-theme 'darktooth))
general
general is a convenient, stable framework for defining your own key-bindings,
especially in evil-mode. I use this to define "leader key" bindings under SPC
in the style of Vim's <leader>, as well as a mode-specific leader bindings
under ,.
(use-package general :demand t :config (general-evil-setup t) (general-override-mode) (general-create-definer d/mode-leader-keys :keymaps 'override :states '(emacs normal visual motion insert) :non-normal-prefix "C-," :prefix ",") (general-create-definer d/leader-keys :keymaps 'override :states '(emacs normal visual motion insert) :non-normal-prefix "C-SPC" :prefix "SPC")) (elpaca-wait)
evil stack
evil is an emacs package that emulates Vim key bindings. It's solid and quite extensible, and there are several packages that enhance the experience or make it more consistent across Emacs.
There may come a day when the ubiquity of Vim fails, when we forsake evil-mode, and break all bonds of verb-noun editing… but it is not this day!
I've considered switching to meow. Part of the problem is that vi/vim is everywhere as a program or as an emulation layer, which makes vim bindings quite a comfy default. Still, meow calls to me…
evil
This is the core evil package. I have some settings here to make it more vimmy, some that make it less I also set some cursor types for indicating what evil state (like vim mode) I'm in.
(use-package evil :demand t :general (mmap "-" 'negative-argument ;; Basically C-[ for a Dvorak keyboard (_ is for terminal). "C-_" 'keyboard-quit "C-/" 'keyboard-quit [escape] 'keyboard-quit) (:states '(insert replace visual) "C-_" 'evil-normal-state "C-/" 'evil-normal-state) (vmap [escape] 'keyboard-quit) :init (setq evil-search-module 'evil-search) :custom (evil-want-C-u-delete t) (evil-want-C-u-scroll t) (evil-want-keybinding nil) (evil-want-fine-undo t) (evil-undo-system 'undo-fu) (evil-symbol-word-search t) (evil-ex-search-vim-style-regexp t) (evil-respect-visual-line-mode t) (evil-split-window-below t) (evil-vsplit-window-right t) :config (setq evil-insert-state-cursor '(bar . 1) ;thinner cursors evil-emacs-state-cursor '(hbar . 1) ;; Set these cursor types for evil-terminal-cursor-changer evil-normal-state-cursor 'box evil-motion-state-cursor 'box evil-visual-state-cursor 'box))
This is a small evil-ex alias for delete-trailing-whitespace, which I use quite
a bit, unless whitespace-cleanup-mode is doing the job for me. I also have an
alias for killing the current buffer, since :bd doesn't quite do what I want as
it also closes the window.
(d/after 'evil (evil-ex-define-cmd "dtw" #'delete-trailing-whitespace) (evil-ex-define-cmd "bk" #'kill-current-buffer))
Ok, let there be Vim!
(d/after 'evil (evil-mode 1))
evil-nerd-commenter
This package provides emulation for Vim's Nerd Commenter; it provides useful utilities for commenting things out.
(use-package evil-nerd-commenter :general (nmap "gc" 'evilnc-comment-operator "gy" 'evilnc-copy-and-comment-operator) (d/leader-keys "ci" 'd/comment-or-uncomment-lines-inverse "cl" 'evilnc-comment-or-uncomment-lines "cp" 'evilnc-comment-or-uncomment-paragraphs "ct" 'evilnc-comment-or-uncomment-to-the-line "cy" 'evilnc-copy-and-comment-lines) :config (defun d/comment-or-uncomment-lines-inverse (&optional arg) "Source: https://git.io/vQKza" (interactive "p") (let ((evilnc-invert-comment-line-by-line t)) (evilnc-comment-or-uncomment-lines arg))))
evil-collection
evil-collection provides evil integration for a bunch of miscellaneous modes for a more consistent experience.
(use-package evil-collection :custom (evil-collection-setup-minibuffer t) (evil-collection-term-sync-state-and-mode-p t) (evil-collection-repl-submit-state 'insert) :init (evil-collection-init))
evil-indent-plus
This package provides evil text objects to manipulate text based on indentation. For example, you can delete the current line and all adjacent lines at the same indentation level.
(use-package evil-indent-plus :general (itomap "i" 'evil-indent-plus-i-indent "I" 'evil-indent-plus-i-indent-up "J" 'evil-indent-plus-i-indent-up-down) (otomap "i" 'evil-indent-plus-a-indent "I" 'evil-indent-plus-a-indent-up "J" 'evil-indent-plus-a-indent-up-down))
evil-lion
evil-lion is an evil-port of vim-lion. It can be used to align text at a particular delimiter.
(use-package evil-lion :general (nvmap "gl" 'evil-lion-left "gL" 'evil-lion-right))
For example:
this text is not aligned at space characters !
Then, press gl<space>:
this text is not aligned at space characters !
evil-matchit
evil-matchit is a port of matchit.vim. It provides a more generic and somewhat
context-aware % binding for jumping between matching brackets, tags, if/else
branches, etc.
(use-package evil-matchit :general (itomap "%" 'evilmi-inner-text-object) (otomap "%" 'evilmi-outer-text-object) (nvmap "%" 'evilmi-jump-items) :config (global-evil-matchit-mode 1))
evil-numbers
evil-numbers provides some operators for incrementing the number at point (or
in selection). I don't actually use it very much, so it's disabled with
org-mode's :tangle no.
(use-package evil-numbers :ensure (:host github :repo "juliapath/evil-numbers") :general (d/leader-keys "n-" 'd/numbers/evil-numbers/dec-at-pt "n=" 'd/numbers/evil-numbers/inc-at-pt) :config (defhydra d/numbers () " ╭─────────╮ │ numbers │ └─────────┘ [_=_] inc [_-_] dec ─────────── " ("=" evil-numbers/inc-at-pt) ("-" evil-numbers/dec-at-pt)))
evil-org
evil-org-mode adds helpful evil-mode keybindings to org-mode for navigating and manipulating org elements.
(use-package evil-org :hook (org-mode . evil-org-mode) :general (nvmap evil-org-mode-map "TAB" 'org-cycle "S-TAB" 'org-cycle) :config (evil-org-set-key-theme) (require 'evil-org-agenda) (evil-org-agenda-set-keys) (nmap evil-org-mode-map [backtab] 'org-shifttab) (d/after 'org-src (define-key org-src-mode-map [remap evil-write] 'org-edit-src-save) (define-key org-src-mode-map [remap evil-save-and-close] (lambda () (interactive) (org-edit-src-save) (org-edit-src-exit))) (define-key org-src-mode-map [remap evil-save-modified-and-close] (lambda () (interactive) (org-edit-src-save) (org-edit-src-exit)))))
evil-cleverparens
I may try this again at some point, but this is disabled for now. I just use electric-pair
(use-package evil-cleverparens :hook (prog-mode . evil-cleverparens-mode))
evil-surround
evil-surround is an evil-mode port of vim-surround. It helps work with surrounding chars better by providing bindings for adding, deleting, or changing surrounding characters (like brackets).
(use-package evil-surround :general (omap "s" 'evil-surround-edit "S" 'evil-Surround-edit) (vmap "S" 'evil-surround-region "gS" 'evil-Surround-region) :config (add-to-list 'evil-surround-operator-alist '(evil-cp-change . change)) (add-to-list 'evil-surround-operator-alist '(evil-cp-delete . delete)))
Ostensibly this was a more minimal integration layer for embrace.el with evil, but I'm not actually sure it's doing anyhting, so it's disabled. See also: evil-embrace.el.
(use-package embrace :after evil-surround :demand t :config (define-advice evil-surround-region (:around (fn beg end type char &optional force-new-line) d/embrace) (if (evil-surround--get-delims char) (funcall fn beg end type char force-new-line) (embrace--add-internal beg end char))) (define-advice evil-surround-delete (:around (fn char &optional outer inner) d/embrace) (if (or (and outer inner) (evil-surround--get-delims char)) (funcall fn char outer inner) (embrace--delete char))))
evil-terminal-cursor-changer
This a package enables changing the cursor type terminals based on the evil-state (like Vim mode), because this can be surprisingly tricky and terminal-dependent.
(use-package evil-terminal-cursor-changer :init (unless (display-graphic-p) (etcc-on)) (add-to-list 'after-make-frame-functions (defun d/maybe-turn-on-cursor-changer (frame) (if (display-graphic-p) (etcc-on)))))
vertico stack
The vertico stack is a minimalist and performant minibuffer completion UI that integrates well with the built-in Emacs completion framework. In some ways it's the spiritual successor to ivy and helm, and a more featureful alternative to the built-in icomplete.
More specifically, it can:
- Suggest and help find emacs commands, file names, buffers, anything…
- Annotate suggestions with extra information
- Provide an alternate ergonomic navigation and search interface (in buffer or external like grep, ripgrep, find, etc.)
I find having a completion framework like this useful for discoverability and ease of use.
vertico
vertico is the main user interface for this stack. It displays completions vertically in the minibuffer.
(use-package vertico :general (nmap vertico-map [escape] 'keyboard-escape-quit "C-/" 'keyboard-escape-quit) (d/leader-keys "SPC" 'execute-extended-command "ff" 'find-file "fa" 'find-alternate-file "fF" 'find-file-other-window "fj" 'project-find-file "hF" 'describe-face) :init (vertico-mode))
consult
consult provides extra search and navigation commands that integrate with completion. I use these commands all the time:
consult-bufferfor switching to buffer with previewconsult-ripgrepfor searching in files with previewconsult-linefor searching the current buffer (also seeconsult-line-multi).consult-yank-popfor kill-ring (like the "clipboard") selection.
Also check out consult-theme for previewing installed themes!
(use-package consult :bind (:map minibuffer-local-map ("M-r" . consult-history)) :general (d/leader-keys "fl" 'consult-locate ;; "hdb" 'counsel-descbinds ;; "iu" 'counsel-unicode-char "sr" 'consult-ripgrep "ss" 'consult-line "sm" 'consult-line-multi "y" 'consult-yank-pop "bb" 'consult-buffer ) :custom (consult-locate-args "locate -i") :config (defun noct-consult-line-evil-history (&rest _) "Add latest `consult-line' search pattern to the evil search history ring. This only works with orderless and for the first component of the search." (when (and (bound-and-true-p evil-mode) (eq evil-search-module 'evil-search)) (let ((pattern (consult--join-regexps (cdr (orderless-compile (car consult--line-history))) 'emacs))) (add-to-history 'evil-ex-search-history pattern) (setq evil-ex-search-pattern (list pattern t t)) (setq evil-ex-search-direction 'forward) (when evil-ex-search-persistent-highlight (evil-ex-search-activate-highlight evil-ex-search-pattern))))) (advice-add #'consult-line :after #'noct-consult-line-evil-history))
orderless
This is basically a fuzzy completion engine, called a completion style in Emacs. It helps you find matches by entering space-separated substrings in any order and/or using regular expressions.
(use-package orderless :custom (completion-styles '(orderless basic)) (completion-category-overrides '(file (styles basic partial-completion))) (orderless-component-separator #'orderless-escapable-split-on-space))
marginalia
marginalia provides details and annotations to minibuffer completions.
(use-package marginalia :init (marginalia-mode))
embark
embark provides alternate actions for minibuffer completions. I won't lie, this one hasn't entirely clicked for me.
I do find embark-export (bound here to C-c C-o) usefull for viewing
consult-ripgrep or consult-line results in their own buffer.
(use-package embark :general (d/leader-keys "iu" 'embark-save-unicode-character) :bind (([S-return] . embark-act) ;; pick some comfortable binding ;; ("C-;" . embark-dwim) ;; good alternative: M-. ("C-h B" . embark-bindings) :map vertico-map ("C-c C-o" . embark-export)) :init ;; Optionally replace the key help with a completing-read interface (setq prefix-help-command #'embark-prefix-help-command) ;; Show the Embark target at point via Eldoc. You may adjust the Eldoc ;; strategy, if you want to see the documentation from multiple providers. ;; (add-hook 'eldoc-documentation-functions #'embark-eldoc-first-target) ;; (setq eldoc-documentation-strategy #'eldoc-documentation-compose-eagerly) :config ;; Hide the mode line of the Embark live/completions buffers (add-to-list 'display-buffer-alist '("\\`\\*Embark Collect \\(Live\\|Completions\\)\\*" nil (window-parameters (mode-line-format . none))))) ;; Consult users will also want the embark-consult package. (use-package embark-consult :hook (embark-collect-mode . consult-preview-at-point-mode))
org-mode
org-mode is a special plain-text mode for taking notes, creating documents, literate programming, todo lists, agenda items, and more. It's plain text, but it handles heading collapsing, aligned tables, and code execution, among other things. It's fantastic, and one of the things that brings people to Emacs.
Of course, this config is written in org-mode. I also use it to publish my blog, take notes with denote (when I remember to), on occasion create presentations… it's a blast. Try org-mode.
Base package
This is the bulk of the base settings, configuring things like how org looks and behaves.
(use-package org :ensure nil :general (nmap org-mode-map "ga" 'org-archive-subtree) (mmap org-mode-map "RET" (general-predicate-dispatch nil (d/org-at-openable-item-p) 'org-open-at-point (org-at-item-checkbox-p) 'org-toggle-checkbox) "S-<return>" (general-predicate-dispatch nil (org-in-src-block-p) 'org-babel-execute-src-block)) :custom ;; Appearance (org-startup-indented t) (org-fontify-quote-and-verse-blocks t) (org-hide-emphasis-markers t) (org-startup-with-inline-images t) (org-ellipsis " …") (org-highlight-latex-and-related '(native entities)) (org-pretty-entities t) (org-pretty-entities-include-sub-superscripts nil) (org-hide-leading-stars t) (org-fontify-done-headline t) (org-image-actual-width 500) ;; Files (org-default-notes-file "~/org/notes.org") (org-archive-location "~/org/archive.org::") ;; Editing (org-M-RET-may-split-line nil) (org-insert-heading-respect-content t) ;; Todo (org-enforce-todo-dependencies t) (org-enforce-todo-checkbox-dependencies t) (org-log-done 'time) (org-log-redeadline 'time) (org-log-reschedule 'time) (org-log-into-drawer t) (org-todo-keywords '((sequence "TODO(t)" "IN-PROGRESS(p)" "WAITING(w)" "|" "DONE(d)" "CANCELED(c)"))) :general (d/mode-leader-keys org-mode-map "$" 'org-archive-subtree "/" 'org-sparse-tree "A" 'org-archive-subtree "R" 'org-refile "^" 'org-sort "c" 'org-capture "g" 'consult-org-heading ;; insert stuff ":" 'org-set-tags-command "f" 'org-footnote-new "." 'org-time-stamp "," 'org-timestamp-inactive "d" 'org-deadline "s" 'org-schedule "P" 'org-set-property ;; editing "xe" 'org-emphasize "xx" 'org-cut-special "xy" 'org-copy-special "xp" 'org-paste-special))
Agenda
The org agenda is as way to view TODOs and timestamps, possibly across multiple files, in an organized agenda buffer. Some people use it to organize their lives!
(use-package org-agenda :ensure nil :custom (org-agenda-files '("~/org/agenda.org")) (org-agenda-text-search-extra-files '(agenda-archives)) (org-agenda-skip-scheduled-if-done t) (org-agenda-skip-deadline-if-done t) ;; (org-agenda-hide-tags-regexp ".*") (org-agenda-span 'week) (org-agenda-deadline-faces '((1.0 . org-warning) (0.5 . org-upcoming-deadline) (0.0 . '(:foreground "#A89984")))) :general (d/mode-leader-keys org-mode-map "a" 'org-agenda))
Lists
Some settings for working with plain lists.
(use-package org-list :ensure nil :custom (org-list-allow-alphabetical t) (org-list-use-circular-motion t) (org-list-demote-modify-bullet '(("-" . "+") ("+" . "*") ("*" . "-"))) :general (d/mode-leader-keys org-mode-map "il" 'org-insert-link))
Links
Settings for links.
(use-package ol :ensure nil :custom (org-link-search-must-match-exact-headline nil) (org-link-elisp-confirm-function 'y-or-n-p))
Source blocks
Source blocks are one of the killer features of org-mode! You can view and edit code as if you were editing in that language's major mode (e.g. viewing a file in that language), and even switch to a proper language buffer for editing.
(use-package org-src :ensure nil :custom (org-edit-src-content-indentation 0) (org-confirm-babel-evaluate nil) (org-src-window-setup 'current-window) :general (d/mode-leader-keys org-mode-map "b" 'org-babel-tangle "'" 'org-edit-special) (d/mode-leader-keys org-src-mode :definer 'minor-mode "'" 'org-edit-src-exit))
You can also run code blocks! Normally, you customize the variable
org-babel-load-languages to individually enable execution of code blocks. I'd
rather play it fast and loose and enable all languages on demand.
(d/after 'org (define-advice org-babel-execute-src-block (:before (&rest _) d/load-lang) "Load src language on demand. This removes the need to add every language manually to `org-babel-load-languages'. This also implies that any language that supports execution can be executed. Executing src blocks is an active enough action that I'm ok with this." (let ((language (intern (org-element-property :language (org-element-at-point))))) (message "LANG: %s" language) (pcase language ('sh (setq language 'shell)) ('C++ (setq language 'C))) (message "LANG: %s" language) (unless (alist-get language org-babel-load-languages) (add-to-list 'org-babel-load-languages (cons language t)) (org-babel-do-load-languages 'org-babel-load-languages org-babel-load-languages)))))
I like to use a prettier name for my source block edit buffers, so I voerride the function that contsructs them.
(define-advice org-src--construct-edit-buffer-name (:override (org-buffer-name lang)) (format "%s → %s" org-buffer-name lang))
For Python blocks, there are two result types: output and value. Value is kind
of weird, in that it wraps the whole block in a function, and you have to add
an explicit return to see results. I don't love it. output acts more like how I
expect, by running the code normally and printing the output.
(d/after 'ob-python (add-to-list 'org-babel-default-header-args:python '(:results . "output")))
This is a helper function I use to implement the generic RET key binding seen above.
(defun d/org-at-openable-item-p () "Return non-nil if item is openable. (Think link-like)" (let* ((context (org-element-lineage (org-element-context) '(clock footnote-definition footnote-reference headline inlinetask link timestamp) t)) (type (org-element-type context))) (memq type '(footnote-definition footnote-reference inlinetask link timestamp))))
Tables
Some key bindings for working with org tables, which are pretty nifty. They can be used as mini-spreadsheets, produce plots, and even be passed as input to code blocks.
(use-package org-table :ensure nil :general (d/mode-leader-keys org-mode-map "ic" 'org-table-insert-column "tb" 'org-table-blank-field "tc" 'org-table-convert "tdc" 'org-table-delete-column "tf" 'org-table-eval-formula "te" 'org-table-export "tic" 'org-table-insert-column "tih" 'org-table-insert-hline "tiH" 'org-table-hline-and-move "tI" 'org-table-import "tH" 'org-table-move-column-left "tL" 'org-table-move-column-right "tn" 'org-table-create "tN" 'org-table-create-with-table.el "tr" 'org-table-recalculate "ts" 'org-table-sort-lines "ttf" 'org-table-toggle-formula-debugger "tto" 'org-table-toggle-coordinate-overlays "tw" 'org-table-wrap-region))
Exports
Exports are the basis for document creation with org-mode. There are many different output format options built-in, and some available as external packages. There's a lot of flexibility and settings in how documents are created.
(use-package ox :ensure nil :custom (org-export-use-babel nil) :general (d/mode-leader-keys org-mode-map "e" 'org-export-dispatch))
HTML
Use HTML5 for exporting to HTML.
(use-package ox-html :ensure nil :custom (org-html-validation-link nil) (org-html-html5-fancy t) (org-html-doctype "html5"))
Latex
It's been a while since I properly worked with latex in org-mode. You should know that you can use inline latex and literal latex blocks for more fine-grained control.
(use-package ox-latex :ensure nil :custom (org-latex-packages-alist '(("" "listings") ("" "color") ("" "tabularx"))) (org-latex-src-block-backend 'listings))
Mouse support
This enables the mouse for things like following links, expanding trees, and a few others I don't really use.
(use-package org-mouse :ensure nil :after org :demand t)
org-appear
This is a package that automatically reveals hidden emphasis markers, links, etc. in org-mode when the cursor is over them. Fair warning: it's a bit broken recently and flickers as you edit links.
(use-package org-appear :hook (org-mode . org-appear-mode) :custom (org-appear-autoemphasis t) (org-appear-autoentities t) (org-appear-autolinks t) (org-appear-autokeywords t) (org-appear-autosubmarkers t))
Bindings
Some global leader bindings for doing stuff. Notably, C-u, or
universal-argument, is used in vanilla Emacs to modify the next command to run
in different ways. Since I have evil-want-C-u-scroll and evil-want-C-u-delete
to get closer to vim bindings, I need a different key for this. I bind it to
SPC u.
(d/leader-keys "." 'abort-recursive-edit "qf" 'delete-frame "qq" 'save-buffers-kill-emacs "td" 'toggle-debug-on-error "tq" 'toggle-debug-on-quit "!" 'shell-command "&" 'async-shell-command ":" 'eval-expression "r" 'repeat "u" 'universal-argument)
Use f11/f12 keys to start/stop recording a keyboard macro. This is a way to record a sequence of editing commands so you can replay it later. If you're a little careful about it, you can use this to avoid having to do very repetitive, manual edits.
Of course, with evil you also get vim-style keyboard macros using q and @.
(general-def "<f11>" 'kmacro-start-macro-or-insert-counter "<f12>" 'kmacro-end-or-call-macro)
These are some minibuffer bindings that mirror my evil C-[ alternative for dvorak.
(general-def (minibuffer-local-map minibuffer-local-ns-map minibuffer-local-completion-map minibuffer-local-must-match-map minibuffer-local-isearch-map) [?\C-/] 'minibuffer-keyboard-quit [?\C-_] 'minibuffer-keyboard-quit [escape] 'minibuffer-keyboard-quit)
This is just so I can repeat the universal argument with the same SPC u
binding. I could instead just use C-u after pressing SPC u once.
(general-def universal-argument-map "SPC u" 'universal-argument-more)
Mode line
(defvar d/selected-window nil) (add-hook 'pre-redisplay-functions (defun d/set-selected-window (_) (unless (minibuffer-window-active-p (selected-window)) (setq d/selected-window (frame-selected-window))))) (defun d/window-active-p () (eq (selected-window) d/selected-window)) (defun d/compact-mode-line-p () (< (window-width) 80)) (defface d/mode-line-dim '((t (:inherit font-lock-comment-face :slant normal))) "Mode line face for dim segments") (defface d/mode-line-half-dim '((t (:foreground "#BDAE93"))) "Mode line face for dim segments") (defface d/mode-line-highlight '((t (:inherit hl-line))) "Mode line mouse face") (defun d/mode-line-face (&optional face inactive-face) (if (d/window-active-p) (or face 'mode-line-active) (or inactive-face 'mode-line-inactive))) (defvar-local d/mode-line-position '((:eval `(:propertize " %4l:" face ,(d/mode-line-face 'd/mode-line-half-dim))) (:eval `(:propertize ,(format "%-2s " (format-mode-line "%c")) face ,(d/mode-line-face 'd/mode-line-half-dim))))) (put 'd/mode-line-position 'risky-local-variable t) (defvar-local d/mode-line-mode `((:propertize (:eval (if (listp mode-name) (car mode-name) mode-name)) mouse-face d/mode-line-highlight local-map ,mode-line-major-mode-keymap) " ")) (put 'd/mode-line-mode 'risky-local-variable t) (defvar-local d/mode-line-anzu '((anzu--state (:eval (propertize (let ((here anzu--current-position) (total anzu--total-matched)) (cond ((eq anzu--state 'replace-query) (format " %d replace " anzu--cached-count)) ((eq anzu--state 'replace) (format " %2d/%d " (1+ here) total)) (anzu--overflow-p (format " %s+ " total)) (t (format " %2s/%d " here total)))) 'face (d/mode-line-face '(:foreground "#83A598"))))))) (put 'd/mode-line-anzu 'risky-local-variable t) (defun d/mode-line-switch-buffer (click) (interactive "e") (when (and (eq (posn-area (event-end click)) 'mode-line) (eq (posn-window (event-end click)) (posn-window (event-start click)))) (with-selected-window (posn-window (event-start click)) (call-interactively #'consult-buffer)))) (defvar-local d/mode-line-buffer-name '((:eval (propertize (cond ((get-buffer-process (current-buffer)) " ") ((buffer-narrowed-p) "▼") (buffer-read-only "⏺") ((buffer-modified-p) "⏺") (t " ")) 'face (d/mode-line-face (cond (buffer-read-only '(:foreground "#D3869B")) ((buffer-modified-p) '(warning)) (t '(:foreground "#B8BB26")))) )) " " (:propertize "%b" face bold help-echo "mouse-1: Switch buffer" mouse-face d/mode-line-highlight local-map (keymap (mode-line keymap (mouse-1 . d/mode-line-switch-buffer)))) " ")) (put 'd/mode-line-buffer-name 'risky-local-variable t) (defvar-local d/mode-line-remote '(:eval (let ((host (file-remote-p default-directory 'host)) (user (file-remote-p default-directory 'user))) (when host (propertize (concat " " user "@" host) 'face (d/mode-line-face '(:foreground "#D3869B" :slant italic) '(:slant italic))))))) (put 'd/mode-line-remote 'risky-local-variable t) (defvar-local d/mode-line-vc '(" " (vc-mode (:eval (let ((value (if (string-prefix-p " Git-" vc-mode) (substring vc-mode 5) vc-mode))) (if (d/window-active-p) value (propertize value 'face nil))))))) (put 'd/mode-line-vc 'risky-local-variable t) (defvar-local d/mode-line-process '(mode-line-process (:eval (propertize (concat (format-mode-line '((mode-line-process ("" mode-line-process " "))))) 'face (d/mode-line-face '(:inherit font-lock-keyword-face :slant normal)))))) (put 'd/mode-line-process 'risky-local-variable t) (defvar-local d/mode-line-coding '((:eval (propertize (pcase (coding-system-eol-type buffer-file-coding-system) (0 "LF") (1 "CRLF") (2 "CR") (_ "")) 'face (d/mode-line-face 'd/mode-line-dim) 'mouse-face 'd/mode-line-highlight 'local-map (let ((map (make-sparse-keymap))) (define-key map [mode-line mouse-1] 'mode-line-change-eol) map))) " " (buffer-file-coding-system (:eval (propertize (upcase (symbol-name (let ((coding-system-name (plist-get (coding-system-plist buffer-file-coding-system) :name))) (pcase coding-system-name ((or 'prefer-utf-8 'undecided) 'utf-8) (_ coding-system-name))))) 'face (d/mode-line-face 'd/mode-line-dim) 'help-echo 'mode-line-mule-info-help-echo 'local-map mode-line-coding-system-map 'mouse-face 'd/mode-line-highlight))))) (put 'd/mode-line-coding 'risky-local-variable t) (defvar d/mode-line-right '(" " (:eval (if (d/compact-mode-line-p) "" mode-line-misc-info)) d/mode-line-process d/mode-line-coding " " d/mode-line-mode )) (put 'd/mode-line-right 'risky-local-variable t) (setq-default mode-line-format `("%e" " " d/mode-line-buffer-name d/mode-line-position d/mode-line-remote d/mode-line-vc d/mode-line-anzu mode-line-format-right-align d/mode-line-right " "))
Help
Built-in
man
The man library uses the man command to browse man pages inside emacs. The
g-prefixed versions here are to prefer a homebrew-installed man-db (what's used
on Linux) if it's installed on macOS.
(use-package man :ensure nil :general (d/leader-keys "hm" 'man) :init (setq Man-notify-method 'aggressive manual-program (if (executable-find "gman") "gman" "man") Man-sed-command (if (executable-find "gsed") "gsed" "sed")))
woman
woman is for browsing man pages without the need for a man executable! Neat.
Sometimes useful.
(use-package woman :ensure nil :general (d/leader-keys "hw" 'woman) :custom (woman-fill-column 79))
which-key
which-key gives you annotated suggestions for what key you may press next when pressing prefix keys.
(use-package which-key :ensure nil :defer 10 :config (which-key-mode))
Packages
helpful
helpful is a slightly more informative version of the built-in help utilities for looking up emacs-lisp function, variable, and key definitions. It provides a little more information, source code, and action buttons.
(use-package helpful :custom (helpful-short-filenames t) :general (general-def "C-h f" 'helpful-callable "C-h v" 'helpful-variable "C-h o" 'helpful-symbol "C-h k" 'helpful-key) (d/leader-keys "hf" 'helpful-callable "hv" 'helpful-variable "hs" 'helpful-symbol "hk" 'helpful-key) :config (evil-set-initial-state 'helpful-mode 'motion))
elisp-refs
A supplementary package to helpful to find references to elisp symbols.
(use-package elisp-refs :config (evil-set-initial-state 'elisp-refs-mode 'motion))
Bindings
More evil leader bindings for built-in help or information functions. Info is particularly useful: you can use it to browse the Emacs, use-package, and other manuals offline from Emacs.
(d/leader-keys "hh" 'help-for-help "hV" 'apropos-value "hc" 'describe-char "hM" 'describe-mode "ht" 'describe-text-properties "hn" 'view-emacs-news "hi" 'info "hev" 'version)
Files/Buffers
Settings
These settings concern how to name two buffers with the same file name in
different parent directories. I prefer path/to/file style over the default file</path/to>
(setq uniquify-buffer-name-style 'forward uniquify-strip-common-suffix nil)
Move focus to help windows when opening them.
(setf help-window-select t)
Not that version control: version-numbered backup files.
(setf version-control t delete-old-versions t)
The after-save-hook is a list of funcions run whenever you save a file. Here,
we add this rather verbose function that does something awesome: if the file
contains shebang (#!) at the top, it will automatically make the file
executable on save! So you don't have to go chmod +x it yourself. I always
forget to do that.
(add-hook 'after-save-hook 'executable-make-buffer-file-executable-if-script-p)
Use an existing Emacs frame to open new files in macOS.
(setf ns-pop-up-frames nil)
Built-in
dired
dired is quite a good text-based file manager. It basically takes the output of
ls -l and makes it interactive, navigable, and editable. It's actually my
favorite file manager.
(use-package dired :ensure nil :general (d/leader-keys "ad" 'dired-other-window) (d/mode-leader-keys dired-mode-map "h" 'dired-omit-mode "d" 'dired-du-mode "c" 'dired-collapse-mode) :custom (dired-dwim-target t) (dired-mouse-drag-files t) (wdired-allow-to-change-permissions 'advanced) :init (setq dired-listing-switches "-DlhA --group-directories-first") (when (member system-type '(darwin berkeley-unix)) (if (executable-find "gls") (setq insert-directory-program "gls") (setq dired-listing-switches "-lhA"))) :config (defun d/dired-remote-listing-switches () (when (file-remote-p dired-directory) (setq-local dired-actual-switches "-lhA"))) (defun d/extra-bindings () (nmap dired-mode-map "~" 'd/dired-home "q" 'd/dired-quit ;; got used to this from ranger "h" 'dired-up-directory "l" 'dired-find-file) (remove-hook 'dired-mode-hook 'd/extra-bindings)) (add-hook 'dired-mode-hook #'d/extra-bindings) (add-hook 'dired-mode-hook #'auto-revert-mode) (add-hook 'dired-mode-hook #'d/dired-remote-listing-switches) (defun d/dired-quit () (interactive) (let ((prev-nondired (seq-find (lambda (spec) (not (eq (buffer-local-value 'major-mode (car spec)) 'dired-mode))) (window-prev-buffers)))) (if prev-nondired (switch-to-buffer (car prev-nondired)) (delete-window)))) (defun d/dired-home () (interactive) (dired "~/")))
dired-x
(use-package dired-x :ensure nil :after dired :demand t :hook (dired-mode . d/dired-maybe-omit) :custom (dired-omit-verbose nil) (dired-omit-files (rx string-start "." (1+ nonl) string-end)) (dired-clean-confirm-killing-deleted-buffers nil) :config (message "WHAT") (defun d/dired-maybe-omit () (when (directory-files default-directory nil "^[^.]") (dired-omit-mode))) (add-to-list 'dired-omit-extensions ".import.scm") (add-to-list 'dired-omit-extensions ".link") )
tramp
Tramp is a special utility that helps you edit remote files and run remote processes from the comfort of Emacs, with the benefit of your local editor settings. It's very well integrated throughout Emacs, including with dired, eshell, and more.
It's extremely useful, but it can also be quite laggy if not configured right. This is an attempt to tame the beast. I'm still having some trouble having it respect shared connections on Linux, though it works in macOS, or if I re-evaluate the block below.
(use-package tramp :ensure nil :custom (tramp-default-method "rsync") (tramp-verbose 2) (tramp-completion-reread-directory-timeout nil) (tramp-auto-save-directory (no-littering-expand-var-file-name "tramp-auto-save/")) (remote-file-name-inhibit-locks t) (tramp-use-scp-direct-remote-copying t) (remote-file-name-inhibit-auto-save-visited t) (tramp-copy-size-limit (* 1024 1024)) (tramp-use-connection-share t) (tramp-ssh-controlmaster-options (concat "-o ControlPath=/tmp/ssh-ControlPath-%%r@%%h:%%p " "-o ControlMaster=auto " "-o ControlPersist=4h")) :config ;; (setq tramp-ssh-controlmaster-options ;; (concat ;; "-o ControlPath=/tmp/ssh-ControlPath-%%r@%%h:%%p " ;; "-o ControlMaster=auto " ;; "-o ControlPersist=4h")) (add-to-list 'tramp-default-proxies-alist '("dieggsy\\.com\\'" "\\`root\\'" "/ssh:%h:")) (setq vc-ignore-dir-regexp (format "\\(%s\\)\\|\\(%s\\)" vc-ignore-dir-regexp tramp-file-name-regexp)) (d/after 'ibuffer (setcdr (last (cdar ibuffer-saved-filter-groups)) `(("Tramp" (filename . ,tramp-file-name-regexp))))) (connection-local-set-profile-variables 'remote-direct-async-process '((tramp-direct-async-process . t))) (connection-local-set-profiles '(:application tramp :protocol "rsync") 'remote-direct-async-process) (connection-local-set-profiles '(:application tramp :protocol "scp") 'remote-direct-async-process) (connection-local-set-profiles '(:application tramp :protocol "ssh") 'remote-direct-async-process) (with-eval-after-load 'compile (remove-hook 'compilation-mode-hook #'tramp-compile-disable-ssh-controlmaster-options)))
bookmark
(use-package bookmark :ensure nil :general (d/leader-keys "fB" 'bookmark-jump-other-window "fb" 'bookmark-jump))
ediff
(use-package ediff :ensure nil :custom (ediff-window-setup-function 'ediff-setup-windows-plain) (ediff-split-window-function 'split-window-horizontally) (ediff-diff-options "-w"))
ibuffer
(use-package ibuffer :ensure nil :general (nmap ibuffer-mode-filter-group-map "TAB" 'ibuffer-toggle-filter-group "<backtab>" 'ibuffer-toggle-filter-group "J" 'ibuffer-forward-filter-group "K" 'ibuffer-backward-filter-group) :config (setq ibuffer-saved-filter-groups '(("Default" ("Dired" (mode . dired-mode)) ("ERC" (mode . erc-mode)) ("Help" (or (mode . helpful-mode) (mode . Man-mode) (predicate . (member major-mode ibuffer-help-buffer-modes)))) ("Emacs Package" (filename . ".*/dotfiles/emacs.d/elpaca/.*")) ("Built in" (and (not (mode . eshell-mode)) (filename . "/gnu.*"))) ("Magit" (derived-mode . magit-mode)) ("Shell" (predicate . (member major-mode '(eshell-mode term-mode vterm-mode)))) ("Process" (process)) ("Other" (name . "^*.**$")) ))) (defun d/setup-ibuffer () (ibuffer-switch-to-saved-filter-groups "Default")) (add-hook 'ibuffer-mode-hook #'d/setup-ibuffer) (setq ibuffer-never-show-predicates '("\\*magit-\\(diff\\|process\\):")))
recentf
(use-package recentf :ensure nil :defer 5 :custom (recentf-exclude '(file-remote-p)) (recentf-max-saved-items 50) :config (recentf-mode))
Packages
dired-hacks
dired-open
(use-package dired-open :general (d/mode-leader-keys dired-mode-map "l" 'dired-open-xdg))
dired-rainbow
(use-package dired-rainbow :after dired :demand t :config (dired-rainbow-define-chmod executable-unix "#B8BB26" "-[rw-]+x.*") ;; Most of these are copied from eza (defvar d/image-extensions '("arw" "avif" "bmp" "cbr" "cbz" "cr2" "dvi" "eps" "fodg" "gif" "heic" "heif" "ico" "j2c" "j2k" "jfi" "jfif" "jif" "jp2" "jpe" "jpeg" "jpf" "jpg" "jpx" "jxl" "kra" "krz" "nef" "odg" "orf" "pbm" "pgm" "png" "pnm" "ppm" "ps" "psd" "pxm" "raw" "qoi" "svg" "tif" "tiff" "webp" "xcf" "xpm")) (defvar d/meta-files (rx (group (or (and (or "README" "Readme" "readme") (? "." (* (not " ")))) (and "Dockerfile" (? "." (* (not " ")))) "Brewfile" "bsconfig.json" "BUILD" "BUILD.bazel" "build.gradle" "build.sbt" "build.xml" "Cargo.toml" "CMakeLists.txt" "composer.json" "configure" "Containerfile" "Earthfile" "flake.nix" "Gemfile" "GNUmakefile" "Gruntfile.coffee" "Gruntfile.js" "jsconfig.json" "Justfile" "justfile" "Makefile" "makefile" "meson.build" "mix.exs" "package.json" "Pipfile" "PKGBUILD" "Podfile" "pom.xml" "Procfile" "pyproject.toml" "Rakefile" "RoboFile.php" "SConstruct" "tsconfig.json" "Vagrantfile" "webpack.config.cjs" "webpack.config.js" "WORKSPACE" "id_dsa" "id_ecdsa" "id_ecdsa_sk" "id_ed25519" "id_ed25519_sk" "id_rsa")))) (defvar d/video-extensions '("avi" "flv" "h264" "heics" "m2ts" "m2v" "m4v" "mkv" "mov" "mp4" "mpeg" "mpg" "ogm" "ogv" "video" "vob" "webm" "wmv")) (defvar d/music-extensions '("aac" "m4a" "mka" "mp2" "mp3" "ogg" "opus" "wma")) (defvar d/lossless-music-extensions '("aif" "aifc" "aiff" "alac" "ape" "flac" "pcm" "wav" "wv")) (defvar d/source-extensions '("applescript" "as" "asa" "awk" "c" "c++" "c++m" "cabal" "cc" "ccm" "clj" "cp" "cpp" "cppm" "cr" "cs" "css" "csx" "cu" "cxx" "cxxm" "cypher" "d" "dart" "di" "dpr" "el" "elm" "erl" "ex" "exs" "f" "f90" "fcmacro" "fcscript" "fnl" "for" "fs" "fsh" "fsi" "fsx" "gd" "go" "gradle" "groovy" "gvy" "h" "h++" "hh" "hpp" "hc" "hs" "htc" "hxx" "inc" "inl" "ino" "ipynb" "ixx" "java" "jl" "js" "jsx" "kt" "kts" "kusto" "less" "lhs" "lisp" "ltx" "lua" "m" "malloy" "matlab" "ml" "mli" "mn" "nb" "p" "pas" "php" "pl" "pm" "pod" "pp" "prql" "ps1" "psd1" "psm1" "purs" "py" "r" "rb" "rs" "rq" "sass" "scala" "scm" "scad" "scss" "sld" "sql" "ss" "swift" "tcl" "tex" "ts" "v" "vb" "vsh" "zig")) (defvar d/crypto-extensions '("age" "asc" "cer" "crt" "csr" "gpg" "kbx" "md5" "p12" "pem" "pfx" "pgp" "pub" "sha1" "sha224" "sha256" "sha384" "sha512" "sig" "signature")) (defvar d/document-extensions '("djvu" "doc" "docx" "eml" "fodp" "fods" "fodt" "fotd" "gdoc" "key" "keynote" "numbers" "odp" "ods" "odt" "pages" "pdf" "ppt" "pptx" "rtf" "xls" "xlsm" "xlsx")) (dired-rainbow-define meta (:foreground "#FABD2F" :underline t) d/meta-files) (dired-rainbow-define image "#af5faf" d/image-extensions) (dired-rainbow-define video "#af5fff" d/video-extensions) (dired-rainbow-define music "#8700d7" d/music-extensions) (dired-rainbow-define lossless-music "#8700ff" d/lossless-music-extensions) (dired-rainbow-define crypto "#87afaf" d/crypto-extensions) (dired-rainbow-define document "#8787ff" d/document-extensions) (dired-rainbow-define source (:foreground "#FABD2F" :bold t) d/source-extensions))
dired-collpase
(use-package dired-collapse :config (defvar dired-collapse-ignore-dirs (list "~/Music" "/ssh:")) (defun d/dired-collapse-mode () (unless (seq-find (lambda (dir) (string-prefix-p (expand-file-name dir) (expand-file-name dired-directory))) dired-collapse-ignore-dirs) (dired-collapse-mode))))
dired-narrow
(use-package dired-narrow :general (d/mode-leader-keys dired-mode-map "n" 'dired-narrow))
diredfl
(use-package diredfl :hook (dired-mode . diredfl-mode) :custom (diredfl-ignore-compressed-flag nil) :init ;; Set this here because somehow diredfl interferes with this (defun dired-pretty-slashes () (push '("\\\\" . ?\\) prettify-symbols-alist) (prettify-symbols-mode 1)) (add-hook 'dired-mode-hook #'dired-pretty-slashes 100))
dired-du
(use-package dired-du :ensure (:host github :repo "emacsmirror/dired-du" :branch "master") :config (setq dired-du-size-format t))
nerd-icons-dired
Turn off for performance reasons
(use-package nerd-icons-dired :hook (dired-mode . nerd-icons-dired-mode))
whitespace-cleanup-mode
(use-package whitespace-cleanup-mode :defer 15 :config (global-whitespace-cleanup-mode))
wgrep
(use-package wgrep :commands wgrep-change-to-wgrep-mode :custom (wgrep-auto-save-buffer t))
Functions
File/Buffer Manipulation
(defun d/copy-file () "Copy file to another location. Source: https://git.io/vQKES" (interactive) (call-interactively #'write-file))
Switching
(defun d/switch-to-scratch () "Switch to scratch buffer." (interactive) (switch-to-buffer "*scratch*")) (defun d/switch-to-messages () "Switch to *Messages* buffer." (interactive) (switch-to-buffer "*Messages*"))
Narrowing
(defun d/narrow-and-set-normal () "Narrow to the region and, if in a visual mode, set normal mode. Source: https://git.io/vQKEx" (interactive) (narrow-to-region (region-beginning) (region-end)) (if (string= evil-state "visual") (progn (evil-normal-state nil) (evil-goto-first-line)))) (defun d/narrow-to-region-or-subtree () "Narrow to a region, if set, otherwise to an Org subtree, if present. Source: https://git.io/vQKuf" (interactive) (cond ((and mark-active (not (= (region-beginning) (region-end)))) (d/narrow-and-set-normal)) ((derived-mode-p 'org-mode) (org-narrow-to-subtree)) ((derived-mode-p 'prog-mode) (narrow-to-defun)))) (defun d/narrow-dwim () "Narrow to a thing or widen based on context. Attempts to follow the Do What I Mean philosophy. Source: https://git.io/vQKuU" (interactive) (if (buffer-narrowed-p) (widen) (d/narrow-to-region-or-subtree)))
Bindings
(d/leader-keys "bK" 'kill-buffer "bM" 'd/switch-to-messages "bR" 'rename-buffer "bS" 'd/switch-to-scratch "bc" 'clone-indirect-buffer-other-window "bi" 'ibuffer "bm" 'kill-matching-buffers "bq" 'kill-buffer-and-window "bv" 'view-mode "fc" 'd/copy-file "nf" 'narrow-to-defun "nn" 'd/narrow-dwim "np" 'narrow-to-page "nr" 'narrow-to-region)
Editing
Settings
(setq-default major-mode 'text-mode fill-column 79 indent-tabs-mode nil ;; tab-width 4 ) (setq sentence-end-double-space nil) (add-hook 'text-mode-hook 'auto-fill-mode)
Tools
Built-in
Input method (mule)
(use-package mule :ensure nil :custom (default-input-method "TeX") :config (defvar d/evil-input-method-title nil) (add-hook 'input-method-activate-hook (defun d/set-evil-input-method-title () (setf d/evil-input-method-title (nth 3 (assoc current-input-method input-method-alist))))) (add-hook 'input-method-deactivate-hook (defun d/unset-evil-input-method-title () (setf d/evil-input-method-title nil))) (defvar-local d/mode-line-mule-info `("" (evil-input-method (:propertize ("" d/evil-input-method-title) help-echo (concat ,(purecopy "Current input method: ") evil-input-method ,(purecopy "\n\ mouse-2: Disable input method\n\ mouse-3: Describe current input method")) local-map ,mode-line-input-method-map mouse-face mode-line-highlight)) ,(propertize "%z" 'help-echo 'mode-line-mule-info-help-echo 'mouse-face 'mode-line-highlight 'local-map mode-line-coding-system-map) (:eval (mode-line-eol-desc))) "Like `mode-line-mule-info' but using `evil-input-method'"))
paren
(use-package paren :ensure nil :hook ((emacs-lisp-mode scheme-mode lisp-mode) . show-paren-mode))
hippie-expand
(use-package hippie-exp :ensure nil :general ("M-/" #'hippie-expand))
electric
(use-package electric :ensure nil :defer 5 :config (electric-indent-mode) (electric-layout-mode))
electric-pair
Okay, I've experimented with frameworks for dealing with parentheses and brackets quite a bit. I've used, tested, or am at least aware of:
- smartparens + evil-smartparens
- lispy + lispyville
- paredit + enhanched-evil-paredit
- evil-cleverparens (maybe I'll try this one again)
- parinfer-rust-mode
And welp. I don't know. They all feel unpolished or heavy and I never quite get
into the flow. Meanwhile, built-in electric-pair-mode stays out of may way,
even if I have to do a bit more manual bracket management. evil-surround helps
some here, and the rest is just good ol' fashined text editing.
(use-package elec-pair :ensure nil :demand t :config (electric-pair-mode))
Packages
corfu
Supposedly better than autocomplete.
(use-package corfu :defer 5 :bind (:map corfu-map ("TAB" . corfu-next) ([tab] . corfu-next) ("S-TAB" . corfu-previous) ([backtab] . corfu-previous) ([return] . corfu-insert)) :custom (corfu-cycle t) (corfu-auto t) (corfu-auto-prefix 1) (global-corfu-minibuffer nil) :config (global-corfu-mode))
cape
(use-package cape :init ;; Add to the global default value of `completion-at-point-functions' which is ;; used by `completion-at-point'. The order of the functions matters, the ;; first function returning a result wins. Note that the list of buffer-local ;; completion functions takes precedence over the global list. (add-hook 'completion-at-point-functions #'cape-dabbrev) (add-hook 'completion-at-point-functions #'cape-file) (add-hook 'completion-at-point-functions #'cape-elisp-block) (add-hook 'completion-at-point-functions #'cape-history) (add-hook 'completion-at-point-functions #'cape-elisp-symbol) ;; (add-hook 'completion-at-point-functions #'cape-emoji) (add-hook 'completion-at-point-functions #'cape-keyword) (add-hook 'completion-at-point-functions #'cape-rfc1345) (add-hook 'completion-at-point-functions #'cape-sgml) ;;(add-hook 'completion-at-point-functions #'cape-tex) ;;(add-hook 'completion-at-point-functions #'cape-abbrev) ;;(add-hook 'completion-at-point-functions #'cape-dict) ;;(add-hook 'completion-at-point-functions #'cape-line) )
evil-mc
(use-package evil-mc :config (global-evil-mc-mode))
flyspell
(use-package flyspell :ensure nil :general (d/mode-leader-keys text-mode-map "F" '(:def d/flyspell/body)) :config (defun d/flyspell-add-to-dictionary () "Add word at point to flyspell dictionary. Source: http://tinyurl.com/k8g9sex" (interactive) (let ((current-location (point)) (word (flyspell-get-word))) (when (consp word) (flyspell-do-correct 'save nil (car word) current-location (caddr word) (caddr word) current-location)))) (defhydra d/flyspell () " ╭──────────╮ │ flyspell │ └──────────┴─────────────────────────────────────────────────────── [_>_] goto-next [_c_] correct-next [_a_] auto-correct-next [_b_] buffer [_<_] goto-prev [_C_] correct-prev [_A_] auto-correct-prev [_d_] dict add ─────────────────────────────────────────────────────────────────── " (">" flyspell-goto-next-error) ("<" flyspell-goto-prev-error) ("c" flyspell-correct-next) ("C" flyspell-correct-previous) ("a" flyspell-auto-correct-word) ("A" flyspell-auto-correct-previous-word) ("b" flyspell-buffer) ("d" d/flyspell-add-to-dictionary)))
hungry-delete
(use-package hungry-delete :defer 5 :config (global-hungry-delete-mode))
undo-fu
(use-package undo-fu :ensure (:protocol https))
unfill
(use-package unfill :general (d/leader-keys "xq" 'unfill-toggle) ([remap fill-paragraph] 'unfill-toggle))
yasnippet
(use-package yasnippet :defer 5 :general (d/leader-keys "iy" 'yas-insert-snippet) :config ;; (imap yas-minor-mode-map ;; "SPC" yas-maybe-expand ;; "S-SPC" (lambda () (interactive) (insert " "))) (setq yas-key-syntaxes (remove "w" yas-key-syntaxes)) (yas-global-mode 1))
Modes
cmake-mode
(use-package cmake-mode)
conf-mode
(use-package conf-mode :ensure nil :hook (conf-mode . d/setup-prog-mode) :init (add-to-list 'auto-mode-alist '("\\.service\\'" . conf-mode)))
csv-mode
(use-package csv-mode :mode "\\.csv\\'" :hook (csv-mode . csv-align-mode))
json-mode
(use-package json-mode :mode "\\.json\\'")
markdown-mode
Syntax highlighting for markdown files.
(use-package markdown-mode :mode "\\.md\\'")
yaml-mode
(use-package yaml-mode :mode "\\.yml\\'")
svnwiki-mode
(use-package svnwiki-mode :general (d/mode-leader-keys svnwiki-mode-map "c" 'd/svnwrap-with-code) :mode "\\.svnwiki\\'" :ensure (:repo "https://depp.brause.cc/svnwiki-mode.git") :config (defun d/svnwrap-with-code () (interactive) (let ((bounds (if (region-active-p) (cons (region-beginning) (region-end)) (bounds-of-thing-at-point 'symbol)))) (goto-char (car bounds)) (insert "{{") (goto-char (+ (cdr bounds) 2)) (insert "}}") (goto-char (car bounds)))))
dockerfile-mode
(use-package dockerfile-mode)
olivetti
(use-package olivetti :custom (olivetti-body-width 90))
Bindings
Make indent-rigidly more vimmy.
(general-def indent-rigidly-map "h" 'indent-rigidly-left "l" 'indent-rigidly-right "H" 'indent-rigidly-left-to-tab-stop "L" 'indent-rigidly-right-to-tab-stop)
Leader keys
(d/leader-keys "xii" 'indent-rigidly "xir" 'indent-region "xj" '(:def d/justify/body) "xls" 'sort-lines "xt" '(:def d/transpose/body) "xc" 'count-words "xs" 'd/shorten-url-at-point "xe" 'd/expand-url-at-point "im" 'insert-kbd-macro)
Navigation
Built-in
goto-addr
(use-package goto-addr :ensure nil :hook (((help-mode org-mode text-mode) . goto-address-mode) ((prog-mode conf-mode) . goto-address-prog-mode)))
display-line-numbers
(use-package display-line-numbers :if (version<= "26" emacs-version) :ensure nil :hook ((prog-mode conf-mode) . display-line-numbers-mode) :custom (display-line-numbers-type 'relative) (display-line-numbers-width-start t) (display-line-numbers-grow-only t) :config (defun d/relative-line-numbers () (when display-line-numbers (setf display-line-numbers 'relative))) (defun d/absolute-line-numbers () (when display-line-numbers (setf display-line-numbers t))) (add-hook 'evil-insert-state-entry-hook #'d/absolute-line-numbers) (add-hook 'evil-normal-state-entry-hook #'d/relative-line-numbers) (add-hook 'window-selection-change-functions (defun d/setup-window-selection-change (_) (let* ((old-window (old-selected-window)) (new-window (selected-window)) (new-buffer (window-buffer new-window)) (old-buffer (window-buffer old-window))) (when (not (eql evil-state 'insert)) (when display-line-numbers (setf display-line-numbers 'relative))) (when (not (eq new-buffer old-buffer)) (when old-buffer (with-current-buffer old-buffer (when display-line-numbers (setf display-line-numbers t)))))))))
winner
(use-package winner :ensure nil :defer 15 :general (evil-window-map [right] 'd/winner/winner-redo [left] 'd/winner/winner-undo) :config (winner-mode) (defhydra d/winner () " ╭────────╮ │ winner │ └────────┴──────────────────── [_<left>_] undo [_<right>_] redo ────────────────────────────── " ("<right>" winner-redo) ("<left>" winner-undo)))
hideshow
(use-package hideshow :ensure nil :config (setq hs-allow-nesting t))
xref
(use-package xref :ensure nil :general (d/leader-keys "jD" 'xref-find-definitions-other-window "jd" 'xref-find-definitions "jr" 'xref-find-references) :custom (xref-show-definitions-function #'xref-show-definitions-completing-read))
tab-bar
(use-package tab-bar :ensure nil :general (d/leader-keys "tt" 'tab-switch "tn" 'tab-new "tx" 'tab-close "tr" 'tab-rename) :custom (tab-bar-auto-width-max '(200 20)) (tab-bar-close-button-show nil) (tab-bar-show 1) (tab-bar-separator "") :config ;; for some reason, :defer in the use-package declaration is doing nothing ;; here? Presumably some previous package requires tab-bar. So I'm just ;; directly using the idle timer since I do want to wait for the icons ;; setting. This is a bit jnk. (run-with-idle-timer 5 nil #'tab-bar-mode) (defface d/tab-bar-accent '((t (:background "#BDAE93"))) "Face for active tab accent") (defface d/tab-bar-inactive-accent '((t :inherit 'tab-bar)) "Face for inactive tab accent") (setf tab-bar-tab-name-format-function (defun d/tab-bar-tab-name-format (tab _index) (concat ;; This is a non-breaking space, because I want an accent at the ;; beginning of the tab, but tab-bar-mode uses the first face ;; present (if it's in tab-bar-auto-width-faces) to determine ;; whether to resize the tab and to style the padding. ;; (propertize "" 'face (funcall tab-bar-tab-face-function tab)) ;; (propertize " " ;; 'face (if (eq (car tab) 'current-tab) ;; 'd/tab-bar-accent ;; 'd/tab-bar-inactive-accent)) (propertize (concat " " (alist-get 'name tab)) 'face (funcall tab-bar-tab-face-function tab))))))
Packages
ace-window
(use-package ace-window :general (nmap :keymaps 'override "\\" 'ace-window) (mmap :keymaps 'override "\\" 'ace-window) :config (setq aw-keys (string-to-list "aoeuidhtns") aw-scope 'visible))
avy
(use-package avy :general (d/leader-keys "jc" 'avy-goto-char-2 "jl" 'avy-goto-line "jw" 'avy-goto-word-1 "jt" 'avy-goto-char-timer) :config (setq avy-keys (string-to-list "aoeuidhtns")))
dumb-jump
(use-package dumb-jump :after xref :commands dumb-jump-xref-activate :custom (dumb-jump-prefer-searcher 'rg) (dumb-jump-force-searcher 'rg) :init (add-hook 'xref-backend-functions #'dumb-jump-xref-activate))
imenu-anywhere
imenu on steroids.
(use-package imenu-anywhere)
shackle
(use-package shackle :defer 15 :demand t :custom (shackle-rules '((inferior-emacs-lisp-mode :popup t) (inferior-scheme-mode :popup t) ("\\*sly-mrepl" :regexp t :popup t))) :config (shackle-mode))
Bindings
(d/leader-keys "jI" 'imenu-anywhere "jf" 'find-function-other-window "ji" 'imenu "jv" 'find-variable-other-window "jj" 'find-library-other-window)
Appearance
Settings
(xterm-mouse-mode) (setq frame-resize-pixelwise t window-divider-default-places t custom-raised-buttons nil use-dialog-box nil) (setq-default truncate-lines t word-wrap t) (setq-default cursor-in-non-selected-windows nil) (setq highlight-nonselected-windows nil) (add-to-list 'default-frame-alist '(internal-border-width . 8)) (setf frame-title-format nil) (when (eql system-type 'darwin) (setf ns-use-proxy-icon nil) (add-to-list 'default-frame-alist '(ns-transparent-titlebar . t)))
Font
(add-to-list 'default-frame-alist '(font . "Iosevka Fixed SS14-11:weight=regular")) (defun d/setup-fonts (&rest ignore) (setq inhibit-compacting-font-caches t use-default-font-for-symbols t) ;; Emoji (cond ((find-font (font-spec :name "Apple Color Emoji")) (set-fontset-font t 'emoji "Apple Color Emoji" nil 'prepend)) ((find-font (font-spec :name "Noto Emoji")) (set-fontset-font t 'emoji "Noto Emoji" nil 'prepend)) ((find-font (font-spec :name "Noto Color Emoji")) (set-fontset-font t 'emoji "Noto Color Emoji" nil 'prepend))) ;; Music (set-fontset-font t '(#x1D100 . #x1d1FF) (font-spec :name "FreeSerif") nil 'prepend)) (when (featurep 'fontset) (d/setup-fonts) (add-to-list 'after-make-frame-functions 'd/setup-fonts))
Built-in
hl-line
(use-package hl-line :ensure nil :hook ((prog-mode emms-browser-mode dired-mode conf-mode) . hl-line-mode) :custom (hl-line-sticky-flag nil))
whitespace
(use-package whitespace :ensure nil :defer 5 :config (setq whitespace-style '(face trailing empty missing-newline-at-eof tabs tab-mark)) (setq whitespace-global-modes '(not erc-mode ses-mode vterm-mode magit-status-mode magit-revision-mode magit-log-mode magit-diff-mode dired-mode)) (global-whitespace-mode))
icons
(use-package icons :ensure nil :custom (icon-preference '(text)))
ansi-color
(use-package ansi-color :ensure nil :custom (ansi-color-bold-is-bright t))
Packages
rainbow-mode
(use-package rainbow-mode :ensure (:host github :repo "emacsmirror/rainbow-mode" :branch "master") :hook (help-mode conf-mode) :config (setq rainbow-x-colors-major-mode-list '(c-mode c++-mode java-mode)))
highlight-numbers
Neat-o
(use-package highlight-numbers :hook (((prog-mode conf-mode) . highlight-numbers-mode) (lisp-mode . (lambda () (highlight-numbers-mode -1))) (json-mode . highlight-numbers--turn-off)))
rainbow-delimiters
Better parentheses coloring
(use-package rainbow-delimiters :hook ((prog-mode conf-mode geiser-repl-mode inferior-scheme-mode inferior-python-mode ielm-mode sly-mrepl-mode) . rainbow-delimiters-mode))
xterm-color
(use-package xterm-color :commands xterm-color-filter :hook ((eshell-before-prompt . d/xterm-color-preserve-properties)) :config (defun d/xterm-color-preserve-properties () (setq xterm-color-preserve-properties t)))
ligature
(use-package ligature :demand t :config ;; Enable all Iosevka ligatures in programming modes (ligature-set-ligatures 'prog-mode '("<---" "<--" "<<-" "<-" "->" "-->" "--->" "<->" "<-->" "<--->" "<---->" "<!--" "<==" "<===" "<=" "=>" "=>>" "==>" "===>" ">=" "<=>" "<==>" "<===>" "<====>" "<!---" "<~~" "<~" "~>" "~~>" "::" ":::" "==" "!=" "===" "!==" ":=" ":-" ":+" "<*" "<*>" "*>" "<|" "<|>" "|>" "+:" "-:" "=:" "<******>" "++" "+++")) ;; Enables ligature checks globally in all buffers. You can also do it ;; per mode with `ligature-mode'. (global-ligature-mode t))
comfy-mode
(use-package comfy-mode :ensure (:repo "git@git.sr.ht:~dieggsy/comfy-mode") :init (comfy-mode 1))
Development
Settings
(defun d/auto-fill-only-comments () (setq-local comment-auto-fill-only-comments t)) (add-hook 'prog-mode-hook #'subword-mode) (add-hook 'prog-mode-hook #'auto-fill-mode) (add-hook 'prog-mode-hook #'d/auto-fill-only-comments) (add-to-list 'auto-mode-alist '("PKGBUILD" . sh-mode)) (add-to-list 'auto-mode-alist '("\\.hook\\'" . conf-mode))
Misc
flycheck
(use-package flycheck :hook (python-mode . flycheck-mode) :general (d/mode-leader-keys prog-mode-map "f" '(:def d/flycheck/body)) :config (define-fringe-bitmap 'flycheck-fringe-bitmap-double-arrow [#b10000000 #b01000000 #b11100000 #b00010000 #b11111000 #b00000100 #b11111110 #b00000100 #b11111000 #b00010000 #b11100000 #b01000000 #b10000000]) (defhydra d/flycheck (:body-pre (progn (flycheck-mode 1) (flycheck-list-errors)) :post (quit-windows-on "*Flycheck errors*")) " ╭──────────╮ │ flycheck │ └──────────┴──────── [_>_] next [_H_] first [_<_] prev [_L_] last ──────────────────── " ("f" flycheck-error-list-set-filter) (">" flycheck-next-error) ("<" flycheck-previous-error) ("H" flycheck-first-error) ("L" (progn (goto-char (point-max)) (flycheck-previous-error)))))
hl-todo
(use-package hl-todo :hook (prog-mode . hl-todo-mode))
Language Support
treesit-auto
(use-package treesit-auto :demand t :custom (treesit-auto-install 'prompt) :config (treesit-auto-add-to-auto-mode-alist 'all) (global-treesit-auto-mode) (add-to-list 'treesit-auto-recipe-list (make-treesit-auto-recipe :lang 'c :ts-mode 'c-ts-mode :remap 'c-mode :url "https://github.com/tree-sitter/tree-sitter-c" :requires 'cpp :abi14-revision "v0.23.0" :ext "\\.c\\'") ) (add-to-list 'treesit-auto-recipe-list (make-treesit-auto-recipe :lang 'cpp :ts-mode 'c++-ts-mode :remap 'c++-mode :requires 'c :url "https://github.com/tree-sitter/tree-sitter-cpp" :revision "v0.22.0" ;; BUG: newer grammar breaks syntax highlighting in `c++-ts-mode' :abi14-revision "v0.23.0" :ext "\\.cpp\\'")) )
C++
Packages
irony
(use-package irony :hook ((c++-mode . irony-mode) (irony-mode . irony-cdb-autosetup-compile-options)))
company-irony
(use-package company-irony :after irony :demand :init (d/after 'company '(add-to-list 'company-backends 'company-irony)))
lsp-mode
(use-package lsp-mode :hook (c++-mode c-mode))
cquery
(use-package cquery :hook (c++-mode . lsp-cquery-enable) :config (setq cquery-executable "/usr/bin/cquery"))
ccls
(use-package ccls :hook (c++-mode . d/enable-ccls) :config (defun d/enable-ccls () (require 'ccls) (lsp)))
lsp-ui
(use-package lsp-ui :hook (lsp-mode . lsp-ui-mode))
Setup
(defalias 'cpp-mode 'c++-mode) (d/after 'cc-mode (setq-default c-basic-offset 4) (c-set-offset 'case-label '+))
Python
Built-in
(use-package python :ensure nil :hook (python-mode . eglot-ensure) :config (d/after 'eglot (add-to-list 'eglot-server-programs `(python-mode . ("uvx" "--from" "basedpyright" "basedpyright-langserver" "--" "--stdio")))))
Packages
pyenv-mode
(use-package pyenv-mode :config (pyenv-mode))
pyenv-mode-auto
(use-package pyenv-mode-auto :after pyenv-mode :demand t)
Bindings
(d/mode-leader-keys python-mode-map "eb" 'python-shell-send-buffer "ef" 'python-shell-send-defun "er" 'python-shell-send-region "eF" 'python-shell-send-file "es" 'python-shell-send-statement)
Setup
(defun d/setup-python () (set (make-local-variable 'comment-inline-offset) 2)) (add-hook 'python-mode-hook #'d/setup-python) (d/after 'python (setq python-shell-completion-native-enable nil))
Lisps
Packages
request
(use-package request) (use-package request-deferred)
flycheck-package
(use-package flycheck-package :after flycheck :demand t :config (flycheck-package-setup))
macrostep
(use-package macrostep :general (d/mode-leader-keys (emacs-lisp-mode-map lisp-mode-map) "m" 'macrostep-expand))
highlight-quoted
(use-package highlight-quoted :hook ((lisp-mode sly-mrepl-mode emacs-lisp-mode) . highlight-quoted-mode))
lispyville
(use-package lispyville ;; :hook ((emacs-lisp-mode scheme-mode lisp-mode) . lispyville-mode) :custom (lispyville-key-theme '(operators c-w c-u prettify (atom-movement t) slurp/barf-lispy additional additional-movement additional-insert)))
Functions
(defun d/eval-surrounding-sexp (levels) "Eval sexp around point, specifying depth with LEVELS. Source: http://tinyurl.com/le6wxuo" (interactive "p") (save-excursion (up-list (abs levels)) (eval-last-sexp nil)))
Bindings
(d/mode-leader-keys emacs-lisp-mode-map "eb" 'eval-buffer "ef" 'eval-defun "er" 'eval-region "es" 'd/eval-surrounding-sexp "el" 'eval-last-sexp)
Setup
(defun d/setup-lisp () (hs-minor-mode) (d/after 'evil-surround (push '(? . ("`" . "'")) evil-surround-pairs-alist))) (add-hook 'lisp-mode-hook #'d/setup-lisp) (add-hook 'emacs-lisp-mode-hook #'d/setup-lisp)
Haskell
(use-package haskell-mode :mode "\\.hs\\'" :config (setq haskell-indentation-layout-offset 4 haskell-indentation-left-offset 4 haskell-indentation-ifte-offset 4))
Racket
(use-package racket-mode)
Rust
rust-mode
(use-package rust-mode)
racer
(use-package racer :hook (rust-mode . racer-mode))
ob-rust
(use-package ob-rust)
Scheme
Built-in
(use-package scheme :ensure nil :mode ("\\.sld\\'" . scheme-mode) :mode ("\\.egg\\'" . scheme-mode) :mode ("\\.release-info\\'" . scheme-mode) :interpreter (("chicken-scheme" . scheme-mode) ("csi" . scheme-mode)) :general (d/mode-leader-keys scheme-mode-map "eb" 'd/scheme-send-buffer "ef" 'scheme-send-definition "er" 'scheme-send-region "el" 'scheme-send-last-sexp "es" 'd/scheme-send-surrounding-sexp) (nmap scheme-mode-map "C-c C-c" 'd/scheme-compile-region-or-definition) :init (defvar d/scheme-extra-keywords ;; Function-like keywords `((,(rx "(" (group (or "define-for-syntax" "define-inline" "define-external" "define-constant" "define-foreign-variable")) symbol-end (* space) (? "(") (? (group (1+ word)))) (1 font-lock-keyword-face) (2 font-lock-function-name-face nil t)) ;; Make consistent with define-syntax (,(rx "(" (group "define-syntax-rule") symbol-end (* space) (? "(") (? (group (1+ word)))) (1 font-lock-keyword-face) (2 font-lock-variable-name-face nil t)) ;; Highlight types (,(rx "(" (group "define-foreign-type") symbol-end (* space) (? (group (1+ word))) (? (* space)) (? (group (1+ word)))) (1 font-lock-keyword-face) (2 font-lock-type-face) (3 font-lock-type-face)) ;; CHICKEN-style keywords ("\\<\\sw+:\\>" . 'font-lock-builtin-face) ;; Hashbang ("\\`\\(#!.*/\\)\\([^ \t\n]+\\)\\( .*\\)?" (1 font-lock-comment-face) (2 font-lock-keyword-face t) (3 font-lock-comment-face t t)) ;; #!key, #!optional, #!rest etc. ("\\<#!\\sw+\\>" . 'font-lock-type-face))) (font-lock-add-keywords 'scheme-mode d/scheme-extra-keywords t) (font-lock-add-keywords 'inferior-scheme-mode d/scheme-extra-keywords t) (defvar d/scheme-simple-keywords `((,(format "[[(]%s\\>" (regexp-opt '("and-let*" "cut" "cute" "define-record-type" "define-values" "letrec*" "set!" "syntax-case" "unless" "when" "assume" "compiler-typecase" "cond-expand" "condition-case" "declare" "define-interface" "define-record" "define-specialization" "define-type" "fluid-let" "foreign-lambda" "foreign-lambda*" "foreign-primitive" "foreign-safe-lambda" "foreign-safe-lambda*" "functor" "handle-exceptions" "let-location" "let-optionals" "let-optionals*" "letrec-values" "module" "define-foreign-record-type" "foreign-value" "foreign-declare" "begin-for-syntax" "define-for-syntax" "include-relative" "import-for-syntax" ;; matchable "match" "match-lambda" "match-lambda*" "match-let" "match-let*" "match-letrec" ;; miscmacros "dotimes" "ecase" "select" "repeat" "while" ;; test "test-assert" "test-begin" "test-end" "test" "test-error" "test-group") 1)) . 1))) (font-lock-add-keywords 'scheme-mode d/scheme-simple-keywords t) (font-lock-add-keywords 'inferior-scheme-mode d/scheme-simple-keywords t) :config (defun d/scheme-send-buffer () (interactive) (scheme-send-region (point-min) (point-max))) (defun d/scheme-send-surrounding-sexp (levels) (interactive "p") (save-excursion (up-list (abs levels)) (scheme-send-last-sexp))) (defun d/scheme-compile-region-or-definition () (interactive) (if (region-active-p) (scheme-compile-region (region-beginning) (region-end)) (scheme-compile-definition))) (setq scheme-program-name "csi -q") ;; chicken core (put 'module 'scheme-indent-function 2) (put 'functor 'scheme-indent-function 2) (put 'begin-for-syntax 'scheme-indent-function 0) (put 'and-let* 'scheme-indent-function 1) (put 'let-optionals 'scheme-indent-function 1) (put 'let-optionals* 'scheme-indent-function 1) (put 'letrec-values 'scheme-indent-function 1) (put 'case-lambda 'scheme-indent-function 0) (put 'catch 'scheme-indent-function 'defun) (put 'assume 'scheme-indent-function 1) (put 'cond-expand 'scheme-indent-function 0) (put 'cut 'scheme-indent-function 1) (put 'cute 'scheme-indent-function 1) (put 'set-read-syntax! 'scheme-indnet-function 1) (put 'set-sharp-read-syntax! 'scheme-indnet-function 1) (put 'condition-case 'scheme-indent-function 1) (put 'handle-exceptions 'scheme-indent-function 2) (put 'with-exception-handler 'scheme-indent-function 1) (put 'with-input-from-pipe 'scheme-indent-function 1) (put 'with-output-to-pipe 'scheme-indent-function 1) (put 'compiler-typecase 'scheme-indent-function 1) (put 'foreign-lambda 'scheme-indent-function 2) (put 'foreign-lambda* 'scheme-indent-function 2) (put 'foreign-safe-lambda 'scheme-indent-function 2) (put 'foreign-safe-lambda* 'scheme-indent-function 2) (put 'foreign-primitive 'scheme-indent-function 2) (put 'let-location 'scheme-indent-function 1) (put 'with-error-output-to-port 'scheme-indent-function 1) ;; matchable (put 'match 'scheme-indent-function 1) (put 'match-let 'scheme-indent-function 1) (put 'match-let* 'scheme-indent-function 1) (put 'match-letrec 'scheme-indent-function 1) (put 'match-lambda 'scheme-indent-function 0) (put 'match-lambda* 'scheme-indent-function 0) ;; miscmacros (put 'while 'scheme-indent-function 1) (put 'select 'scheme-indent-function 1) (put 'dotimes 'scheme-indent-function 1) (put 'repeat 'scheme-indent-function 1) (put 'ecase 'scheme-indent-function 1) ;; test (put 'test-assert 'scheme-indent-function 1) (put 'test-error 'scheme-indent-function 1) (put 'test 'scheme-indent-function 1) (put 'test-group 'scheme-indent-function 1) ;; spiffy (put 'with-headers 'scheme-indent-function 1) (put 'with-egg 'scheme-indent-function 1))
Packages
chicken-doc.el
(use-package chicken-doc :general (d/mode-leader-keys scheme-mode-map "d" 'chicken-doc-describe) (d/mode-leader-keys inferior-scheme-mode-map "d" 'chicken-doc-describe) :ensure (:repo "https://depp.brause.cc/chicken-doc.el.git"))
Lisp
Built-in
(use-package lisp-mode :ensure nil :mode "\\.\\(tdf\\|tv-tic\\|tv-diag\\|tv-desc\\|cv-desc\\)\\'")
Packages
sly
(use-package sly :general (d/leader-keys "as" 'd/sly) (nmap sly-mode-map "gd" 'd/sly-edit-or-evil-goto-definition) (:keymaps 'sly-mrepl-mode-map "M-r" 'consult-history) (imap sly-mrepl-mode-map [up] 'sly-mrepl-previous-input-or-button [down] 'sly-mrepl-next-input-or-button) (d/mode-leader-keys lisp-mode-map "M" 'sly-macroexpand-1-inplace "eb" 'sly-eval-buffer "ef" 'sly-eval-defun "er" 'sly-eval-region ;; "es" 'd/eval-surrounding-sexp "el" 'sly-eval-last-expression) :config (defun d/sly () (interactive) (let ((current-prefix-arg '-)) (call-interactively #'sly))) (setq sly-lisp-implementations '((sbcl ("sbcl" "--dynamic-space-size" "8912")) (alisp ("alisp")) (ecl ("ecl")) (clisp ("clisp"))) sly-contribs (append '(sly-tramp sly-macrostep) sly-contribs)) (d/after 'sly-mrepl (add-hook 'sly-mrepl-output-filter-functions 'ansi-color-apply)) (defun d/sly-edit-or-evil-goto-definition () (interactive) (condition-case nil (call-interactively 'sly-edit-definition) (error (evil-goto-definition)))) (define-advice sly--mode-line-format (:override () d/sly-mode-line) (let* ((conn (sly-current-connection)) (conn (and (process-live-p conn) conn)) (name (or (and conn (sly-connection-name conn)) "*")) (pkg (sly-current-package)) (package-name (and pkg (sly--pretty-package-name pkg)))) `((:eval (propertize ,name 'face (when (d/window-active-p) '(:inherit font-lock-keyword-face :slant normal)))) (:eval (propertize ":" 'face (when (d/window-active-p) '(:inherit font-lock-comment-face :slant normal)))) (:eval (propertize ,(or package-name "*") 'face (when (d/window-active-p) '(:inherit font-lock-constant-face :slant normal)))))))) (use-package sly-macrostep)
slime
(use-package slime :general (d/leader-keys "as" 'd/slime) (:keymaps 'slime-repl-mode-map "M-r" 'consult-history) (imap slime-repl-mode-map [up] 'slime-repl-previous-input [down] 'slime-repl-next-input) :hook (lisp-mode . slime-mode) :config (defun d/slime () (interactive) (let ((current-prefix-arg '-)) (call-interactively #'slime))) (d/after 'slime-presentations (define-key slime-presentation-map [return] 'slime-inspect-presentation-at-point) (define-key slime-presentation-map [mouse-1] 'slime-inspect-presentation-at-point)) (require 'slime-macrostep) (add-to-list 'mode-line-misc-info ;; slime-repl-mode doesn't actually set the variable ;; slime-repl-mode for some reason '(:eval (when (or (eql major-mode 'slime-repl-mode) slime-mode) (concat (slime-modeline-string) " ")))) (setq inferior-lisp-program (executable-find "sbcl") slime-lisp-implementations '((sbcl ("sbcl" "--dynamic-space-size" "8912")) (alisp ("alisp")) (ecl ("ecl")) (clisp ("clisp"))) slime-contribs (append '(slime-macrostep slime-tramp slime-cl-indent) slime-contribs)) (define-advice slime-maybe-complete-as-filename (:override ()) "Don't hook into slime's file completion mechanism. It's a bit too agressive and cape covers this anyway." nil)) (d/after 'consult (add-to-list 'consult-mode-histories '(slime-repl-mode slime-repl-input-history slime-repl-input-history-position nil))) ;; (use-package slime-company ;; :after slime ;; :autoload company-slime ;; :custom ;; (slime-company-completion 'fuzzy) ;; :init ;; (d/after 'cape ;; (add-hook 'slime-repl-mode-hook (lambda () (add-to-list 'completion-at-point-functions (cape-company-to-capf #'company-slime)))) ;; (add-hook 'slime-mode-hook (lambda () (add-to-list 'completion-at-point-functions (cape-company-to-capf #'company-slime))))))
cl-fancy
(use-package cl-fancy :ensure (:repo "git@git.sr.ht:~dieggsy/cl-fancy") :hook lisp-mode :after lisp-mode :demand t)
Functions
(defun d/remote-alisp () (interactive) (let* ((remote-host (read-from-minibuffer "Remote host: ")) (name (generate-new-buffer-name "*remote-alisp*")) (forward-proc (concat name "_forward"))) (make-process :name name :buffer name :command `("ssh" ,remote-host "alisp --qq -L /user/dmundo/remote-alisp/start-slynk.lisp") :filter (lambda (proc output) (when (string-match ";; Slynk started at port: \\([[:digit:]]+\\)" output) (let ((port (match-string 1 output))) (make-process :name forward-proc :command `("ssh" ,(format "-L%s:localhost:%s" port port) ,remote-host "echo port-ready") :filter (lambda (proc output) (when (string-match-p "port-ready" output) (sly-connect "localhost" (string-to-number port))))))) (internal-default-process-filter proc output)))))
Ocaml
tuareg
(use-package tuareg :general (d/mode-leader-keys tuareg-mode-map "es" 'tuareg-eval-phrase "eb" 'tuareg-eval-buffer "er" 'tuareg-eval-region))
dune
(use-package dune)
merlin
(use-package merlin :hook (tuareg-mode . merlin-mode))
vimrc-mode
(use-package vimrc-mode)
web-mode
(use-package web-mode :mode ("\\.html?\\'" "\\.xml\\'" "\\.launch\\'") :config (setq web-mode-markup-indent-offset 2))
lua-mode
(use-package lua-mode)
fish-mode
(use-package fish-mode)
java
Packages
(use-package eglot-java :hook (java-mode . eglot-java-mode) :custom (eglot-java-user-init-opts-fn 'd/eglot-java-init) :init (defun d/eglot-java-init (server eglot-java-eclipse-jdt) '(:settings (:java (:configuration :home "/opt/homebrew/opt/openjdk@21/libexec/openjdk.jdk/Contents/Home" ) ;; (:home "/Library/Java/JavaVirtualMachines/temurin-17.jdk/Contents/Home") ))))
gitlab-ci
(use-package gitlab-ci-mode)
robot
(use-package robot-mode :config (defun d/setup-robot () (make-local-variable 'whitespace-style) (add-to-list 'whitespace-style 'spaces 'append) (add-to-list 'whitespace-style 'space-mark 'append)) (add-hook 'robot-mode-hook #'d/setup-robot) (d/after 'eglot (add-to-list 'eglot-server-programs '(robot-mode . ("robotframework_ls")))))
Version Control
diff-hl
(use-package diff-hl :defer 10 :custom (diff-hl-fringe-bmp-function 'd/diff-hl-bitmap) (diff-hl-dired-fringe-bmp-function 'd/diff-hl-bitmap) :init (define-fringe-bitmap 'd/diff-hl-bitmap (make-vector 29 #b00001111)) (defun d/diff-hl-bitmap (type pos) 'd/diff-hl-bitmap) :config (global-diff-hl-mode) (add-hook 'dired-mode-hook #'diff-hl-dired-mode) (d/after 'magit (add-hook 'magit-post-refresh-hook #'diff-hl-magit-post-refresh)))
magit
Like git, for emacs. But cooler. (Just trust me on this one.)
(use-package transient :custom (transient-default-level 7)) (use-package magit :general (d/leader-keys "gB" 'magit-blame "gC" 'magit-clone "gL" 'magit-log-buffer-file "ga" 'magit-submodule-add "gb" 'magit-branch "gc" 'magit-checkout "gf" 'magit-find-file "gl" 'magit-log-all "gs" 'magit-status "gp" 'magit-file-dispatch) :init (setq vc-follow-symlinks t auto-revert-check-vc-info t) :custom (magit-repository-directories '(("~/dotfiles" . 0) ("~/src" . 1) ("~/src/chicken/chicken-eggs/" . 1))) (magit-diff-refine-hunk t) (git-commit-summary-max-length 50) (git-commit-major-mode 'org-mode) (magit-tramp-pipe-stty-settings 'pty) (magit-commit-show-diff nil) (magit-branch-direct-configure nil) (magit-tramp-pipe-stty-settings 'pty) (magit-process-apply-ansi-colors t) :config (defun d/setup-git-commit () (setq-local org-hide-emphasis-markers nil org-pretty-entities nil)) (add-hook 'git-commit-mode-hook #'d/setup-git-commit) (add-hook 'git-commit-setup-hook #'git-commit-turn-on-flyspell))
magit-todos
(use-package magit-todos :after magit :config (magit-todos-mode 1))
smerge
(use-package smerge-mode :ensure nil :general (nmap smerge-mode-map "]]" 'd/smerge/smerge-next "[[" 'd/smerge/smerge-prev) (smerge-mode-map "C-<return>" 'smerge-keep-current) :config (defhydra d/smerge () " ╭────────╮ │ smerge │ └────────┴────────────────────────────── [_]]_] next [_>_] keep-lower [_._] keep-base [_[[_] prev [_<_] keep-upper [_._] keep-all ──────────────────────────────────────── " ("]]" smerge-next) ("[[" smerge-prev) (">" smerge-keep-lower) ("<" smerge-keep-upper) ("." smerge-keep-base) ("a" smerge-keep-all)))
Tools
proced
(use-package proced :ensure nil :custom (proced-auto-update-interval 1) (proced-format 'medium) (proced-filter 'user) (proced-auto-update-flag 'visible) (proced-enable-color-flag t) (proced-descend t))
comint
(setq comint-prompt-read-only t comint-highlight-input nil)
grep
(use-package grep :ensure nil :config (grep-apply-setting 'grep-use-null-device nil) (grep-apply-setting 'grep-template "rg --color=auto --no-heading -nH --null -g '<F>' -e <R>") (grep-apply-setting 'grep-command "rg --color=auto --no-heading -nH --null -e ") (grep-apply-setting 'grep-find-command '("find . -type f -exec rg --color=auto --no-heading -nH --null -e \\{\\} +" . 65)) (grep-apply-setting 'grep-find-template "find -H <D> <X> -type f <F> -exec rg --no-heading -nH --null -e <R> \\{\\} +"))
calc
And it's amazing, to be quite honest. "The poor man's Mathematica", I once heard (and can confirm).
(use-package calc :ensure nil :general (d/leader-keys "ac" 'calc-dispatch) (emap calc-mode-map "x" (lambda () (interactive) (counsel-M-x "^calc-"))) :init (add-hook 'calc-embedded-mode-hook #'d/calc-embedded-set-open-close) :config (evil-set-initial-state 'calc-mode 'emacs) (setq calc-multiplication-has-precedence nil calc-symbolic-mode t) (defun d/calc-embedded-set-open-close () (when comment-start (setq-local calc-embedded-open-mode (concat comment-start " "))) (when comment-end (setq-local calc-embedded-close-mode (concat comment-end "\n")))))
async
(use-package async :after dired :demand t :config (dired-async-mode 1))
htmlize
(use-package htmlize)
eshell
Builtin
(use-package eshell :ensure nil :custom-face (eshell-prompt ((t (:foreground unspecified :inherit default)))) (eshell-ls-backup ((t :foreground unspecified :inherit dired-ignored))) :custom (eshell-modules-list '(eshell-alias eshell-banner eshell-basic eshell-cmpl eshell-dirs eshell-elecslash eshell-extpipe eshell-glob eshell-hist eshell-ls eshell-pred eshell-prompt eshell-script eshell-term eshell-unix)) (eshell-scroll-to-bottom-on-input t) (eshell-hist-ignore-dups t) (eshell-history-size 20000) (eshell-destroy-buffer-when-process-dies t) (eshell-cmpl-compare-entry-function #'string-lessp) :init (defun d/consult-completion () (setq-local completion-in-region-function #'consult-completion-in-region)) (add-hook 'eshell-mode-hook #'d/consult-completion) :config (require 'dired) (require 'diredfl) (defun d/disable-corfu () (corfu-mode -1)) (add-hook 'eshell-mode-hook #'d/disable-corfu) (d/after 'em-hist (define-key eshell-hist-mode-map (kbd "M-r") #'consult-history)) (d/after 'em-term (dolist (visual-command '("ncdu")) (add-to-list 'eshell-visual-commands visual-command))) (defalias #'eshell/clear #'eshell/clear-scrollback) (d/after 'em-ls (defun d/esh-ls-match-meta (file attrs) (string-match-p (concat (rx bos) d/meta-files (rx eos)) file)) (defun d/esh-ls-match-image (file attrs) (string-match-p (concat "\\." (regexp-opt d/image-extensions) "\\'") file)) (defun d/esh-ls-match-video (file attrs) (string-match-p (concat "\\." (regexp-opt d/video-extensions) "\\'") file)) (defun d/esh-ls-match-music (file attrs) (string-match-p (concat "\\." (regexp-opt d/music-extensions) "\\'") file)) (defun d/esh-ls-match-lossless-music (file attrs) (string-match-p (concat "\\." (regexp-opt d/lossless-music-extensions) "\\'") file)) (defun d/esh-ls-match-crypto (file attrs) (string-match-p (concat "\\." (regexp-opt d/crypto-extensions) "\\'") file)) (defun d/esh-ls-match-document (file attrs) (string-match-p (concat "\\." (regexp-opt d/document-extensions) "\\'") file)) (defun d/esh-ls-match-source (file attrs) (string-match-p (concat "\\." (regexp-opt d/source-extensions) "\\'") file)) (add-to-list 'eshell-ls-highlight-alist '(d/esh-ls-match-meta . dired-rainbow-meta-face)) (add-to-list 'eshell-ls-highlight-alist '(d/esh-ls-match-image . dired-rainbow-image-face)) (add-to-list 'eshell-ls-highlight-alist '(d/esh-ls-match-video . dired-rainbow-video-face)) (add-to-list 'eshell-ls-highlight-alist '(d/esh-ls-match-music . dired-rainbow-music-face)) (add-to-list 'eshell-ls-highlight-alist '(d/esh-ls-match-lossless-music . dired-rainbow-lossless-music-face)) (add-to-list 'eshell-ls-highlight-alist '(d/esh-ls-match-crypto . dired-rainbow-crypto-face)) (add-to-list 'eshell-ls-highlight-alist '(d/esh-ls-match-document . dired-rainbow-document-face)) (add-to-list 'eshell-ls-highlight-alist '(d/esh-ls-match-source . dired-rainbow-source-face))) (define-advice eshell-send-input (:before (_) d/expand-abbrev) (expand-abbrev)) (defun d/eshell-expand-abbrev () "Expand command abbrev sorta like fish abbreviations. This should just be a copy of abbrev--default-expand that adds the condition of only expanding abbrevs at the beginning of the line." (pcase-let ((`(,sym ,name ,wordstart ,wordend) (abbrev--before-point))) (when (and sym (save-excursion (goto-char wordstart) (forward-comment (- (buffer-size))) (or (save-excursion (backward-char 1) (looking-at-p "|")) (save-excursion (forward-char 1) (= (point) (line-beginning-position)))))) (abbrev--default-expand)))) (defun d/eshell-abbrevs () (setq-local abbrev-expand-function #'d/eshell-expand-abbrev)) (add-hook 'eshell-mode-hook #'abbrev-mode) (add-hook 'eshell-mode-hook #'d/eshell-abbrevs))
egp
(use-package egp :after eshell :commands egp-theme :ensure nil :custom (eshell-prompt-function #'egp-theme))
esh-ls-colors
(use-package esh-ls-colors :after em-ls :demand t :ensure nil)
esh-more-commands
(use-package esh-more-commands :after eshell :demand t :ensure nil)
eshell-syntax-highlighting
(use-package eshell-syntax-highlighting :after eshell :demand t :hook eshell-mode)
esh-autosuggest
(use-package esh-autosuggest :hook (eshell-mode . esh-autosuggest-mode))
eat
(use-package eat :after eshell :demand t :init (with-eval-after-load 'eshell (eat-eshell-mode +1) (eat-eshell-visual-command-mode +1)))
denote
(use-package denote :custom (denote-directory (expand-file-name "~/org/denote/")) (denote-prompts '(subdirectory title keywords))) (use-package denote-org) (use-package consult-denote)
Emacs Enhancements
crux
(use-package crux :general (d/leader-keys "TAB" 'crux-switch-to-previous-buffer "fd" 'crux-delete-file-and-buffer "fr" 'crux-rename-file-and-buffer "bs" 'crux-sudo-edit) (d/mode-leader-keys emacs-lisp-mode-map "eR" 'crux-eval-and-replace) :config (crux-with-region-or-line eval-region) (crux-with-region-or-buffer indent-region) (crux-with-region-or-buffer untabify) (crux-with-region-or-buffer tabify) (crux-with-region-or-buffer fill-region))
persistent-scratch
(use-package persistent-scratch :defer 10 :config (with-current-buffer "*scratch*" (emacs-lisp-mode)) (persistent-scratch-setup-default))
Web
(defun d/paste-region-or-buffer () (interactive) (let* ((buffer (current-buffer)) (region-active (region-active-p)) (start (if region-active (region-beginning) (point-min))) (end (if region-active (region-end) (point-max))) (file-name (buffer-file-name)) (name (and file-name `("-n" ,file-name)))) (with-temp-buffer (let ((temp-buffer (current-buffer))) (with-current-buffer buffer (call-process-region start end "hut" nil temp-buffer nil "paste" "create"))) (kill-new (buffer-substring (point-min) (point-max))))))
ERC
(use-package erc :ensure nil :custom (erc-hide-list '("JOIN" "PART" "QUIT")) (erc-modules '(autojoin button completion fill imenu irccontrols keep-place list match menu move-to-prompt netsplit networks nicks notifications readonly ring scrolltobottom spelling stamp track)) :init (defun erc-connect () (interactive) (erc-tls :nick "dieggsy" :server "chat.sr.ht" :port 6697 :user "dieggsy/irc.libera.chat" :password (car (process-lines "op" "read" "op://Private/Sourcehut/chat.sr.ht token")))))
Local
At the end I load a file called local.el, which is local to each machine and
not version controlled. I use this for simple configuration that isn't shared,
like setting font sizes for particular displays or other computer-specific
configuration (e.g. home desktop, personal server, work laptop, work server).
(let ((local-config (locate-user-emacs-file "local.el"))) (when (file-exists-p local-config) (load local-config)))
Local vars
# Local Variables: # eval: (add-hook 'after-save-hook 'org-babel-tangle 'append 'local) # End: