diff options
Diffstat (limited to 'emacs/.emacs.d/init.org')
| -rw-r--r-- | emacs/.emacs.d/init.org | 998 |
1 files changed, 998 insertions, 0 deletions
diff --git a/emacs/.emacs.d/init.org b/emacs/.emacs.d/init.org new file mode 100644 index 0000000..71ca9cc --- /dev/null +++ b/emacs/.emacs.d/init.org @@ -0,0 +1,998 @@ +#+TITLE: Emacs Configuration + +First, lexical binding must be enabled. This is better for performance, but also makes variable binding behave more as expected, and allows the creation of closures. + +#+begin_src emacs-lisp :padline no + ;; -*- lexical-binding: t; -*- +#+end_src + +Switch prompts for “yes” or “no” to “y” or “n”. It's not recommended to do this because it's very easy to accidentally press either single character when you don't realize Emacs is asking you for a confirmation, but in practice I've had no issues with this. At worst I've said “no” to saving a file before doing something a few times. + +#+begin_src emacs-lisp + (defalias 'yes-or-no-p 'y-or-n-p) +#+end_src + +Store all auto-save files in =$XDG_DATA_HOME/emacs/auto-save-list= and backup files in =$XDG_DATA_HOME/emacs/backup-files= to prevent them from clogging up =$XDG_CONFIG_HOME/emacs/=. The XDG specification says that if =XDG_DATA_HOME= hasn't specified a default of =~/.local/share= should be used. + +#+begin_src emacs-lisp + (defun oni-core-data-dir (file) + (expand-file-name file (concat (or (getenv "XDG_DATA_HOME") + "~/.local/share") + "/emacs/"))) + + (add-to-list + 'auto-save-file-name-transforms + `(,(rx (zero-or-more any)) + ,(oni-core-data-dir "auto-save-list") + t) + t) + + (setq backup-directory-alist + `((".*" . ,(oni-core-data-dir "backups/")))) + + (setq abbrev-file-name (oni-core-data-dir "abbrev_defs")) + + (setq recentf-save-file (oni-core-data-dir "recentf")) +#+end_src + +Use =hunspell= instead of the default =aspell=. Hunspell is used by LibreOffice and other programs too. So far (at least through Emacs) it doesn't recognize the =’= as an apostrophe and I have to use ='= in org-mode if I want spell checking to accept word contractions in English. + +#+begin_src emacs-lisp + (eval-when-compile (require 'ispell)) + (with-eval-after-load 'ispell + (setq ispell-program-name "hunspell" + ispell-really-hunspell t)) +#+end_src + +Electric quote uses pretty quoting characters =’=, =‘=, =”=, and =“= instead of ='= and ="=, but when I'm in the middle of a word I don't want to use =’= (right single quotation mark), but ='= (apostrophe), which provides better results with spellchecking. So here is a function that checks whether the character before the previous is a word character. + +#+begin_src emacs-lisp + (defun oni-in-word-p () + "Check whether the character just typed was part of a word." + (save-excursion + (backward-char) + (looking-back (rx word) (1- (point))))) +#+end_src + +Now that I can check whether or not I'm typing a word, I can tell =electric-quote-mode= not to use =’= in that case. + +#+begin_src emacs-lisp + (with-eval-after-load 'electric + (add-hook 'electric-quote-inhibit-functions #'oni-in-word-p)) +#+end_src + +So now that that's settled, I should be writing ='= in words like “shouldn't'' and =’= otherwise. And spellchecking should work just fine. + +Add MELPA to package archives, this lets me install more packages. Also my own oni archive, which contains most of my configuration variables. + +#+begin_src emacs-lisp + (use-package package + :if (eq system-type 'darwin) + :demand t + :config + (add-to-list 'package-archives '("melpa" . "https://melpa.org/packages/") t) + (add-to-list 'package-archives '("oni" . "https://ryuslash.srht.site/emacs-config/packages/"))) +#+end_src + +Make use-package default to ensuring everything. This way each =use-package= doesn't need to have its own ~:ensure t~ and when I use Guix to install these modules they don't try to download everything from everywhere else. + +#+begin_src emacs-lisp + (eval-and-compile + (require 'use-package-ensure) + (setq use-package-always-ensure t)) +#+end_src + +* exec-path-from-shell + +This is necessary to make sure that packages installed through bundle and such are available in Emacs. + +#+begin_src emacs-lisp + (use-package exec-path-from-shell + :init + (when (memq window-system '(mac ns x)) + (exec-path-from-shell-initialize))) +#+end_src + +* Ensure system packages + +Add the =use-package-ensure-system-package= extension for =use-package=. + +#+begin_src emacs-lisp + (use-package use-package-ensure-system-package) + (use-package system-packages) +#+end_src + +* Docker + +I need to use Docker for some projects at work. + +#+begin_src emacs-lisp + (use-package dockerfile-mode) +#+end_src + +* PHP + +I currently develop in PHP. + +#+begin_src emacs-lisp + (use-package php-mode + :ensure-system-package php) +#+end_src + +* Magit + +GIT. + +#+begin_src emacs-lisp + (use-package magit + :hook + (magit-pre-display-buffer-hook . magit-save-window-configuration) + :custom + (magit-display-buffer-function 'magit-display-buffer-fullframe-status-v1 "Show magit buffers in full-frame") + (magit-bury-buffer-function 'magit-restore-window-configuration "Bring back the windows as they were before magit")) +#+end_src + +Tell magit to apply ANSI colors in the process buffer. + +#+begin_src emacs-lisp + (use-package magit-process + :config + (setq magit-process-apply-ansi-colors 'filter)) +#+end_src + +* Lua mode + +Lua is mainly used by wezterm right now. + +#+begin_src emacs-lisp + (use-package lua-mode) +#+end_src + +* Markdown + +#+begin_src emacs-lisp + (use-package markdown-mode) +#+end_src + +Also install =markdown-indent-mode= which is very much like =org-indent-mode= (built-in), but for Markdown. + +#+begin_src emacs-lisp + (use-package markdown-indent-mode + :hook markdown-mode) +#+end_src + +* Org Mode + +#+begin_src emacs-lisp + (use-package org + :bind (("C-c o c" . org-capture) + ("C-c o a" . org-agenda) + ("C-c o l" . org-store-link) + ("C-c o b" . org-switchb)) + :config + (add-hook 'org-mode-hook 'visual-line-mode) + (add-hook 'org-mode-hook 'org-indent-mode)) + + (use-package org-capture + :config + (map-put org-capture-templates "t" + '("A simple TODO item" entry (file "") "* TODO %? + :PROPERTIES: + :CREATED: %U + :END:")) + (map-put org-capture-templates "d" + '("A simple DONE item" entry (file "") "* DONE %? + :PROPERTIES: + :CREATED: %U + :END:")) + (map-put org-capture-templates "n" + '("A simple note" entry (file "") "* Note taken on %U + :PROPERTIES: + :CREATED: %U + :END: + + %?")) + (map-put org-capture-templates "N" + '("A simple note (on current task)" item (clock) "- Note taken on %U: \\\\ + %?")) + (map-put org-capture-templates "r" + '("An Operational Request came in" entry (file "") "* TODO Operation Request %^U + :PROPERTIES: + :CREATED: %U + :END: + + %c"))) +#+end_src + +* Diminish + +This is used to hide minor mode names. + +#+begin_src emacs-lisp + (use-package diminish) +#+end_src + +* GCMH + +This package helps improve the garbage collection settings to match modern computers better and improve overall performance. + +#+begin_src emacs-lisp + (use-package gcmh + :diminish gcmh-mode + :init + (gcmh-mode)) +#+end_src + +* Hydra + +#+begin_src emacs-lisp + (use-package hydra) +#+end_src + +* Delete Trailing Whitespace + +It bugs me to have whitespace left over. + +#+begin_src emacs-lisp + (add-hook 'before-save-hook 'delete-trailing-whitespace) +#+end_src + +* Consult + +#+begin_src emacs-lisp + (use-package consult + :bind (("M-g M" . consult-global-mark) + ("M-g m" . consult-mark) + ([remap bookmark-jump] . consult-bookmark) + ([remap goto-line] . consult-goto-line) + ([remap imenu] . consult-imenu) + ([remap project-switch-to-buffer] . consult-project-buffer) + ([remap projectile-switch-to-buffer] . consult-project-buffer) + ([remap switch-to-buffer] . consult-buffer) + ([remap yank-pop] . consult-yank-pop))) +#+end_src + +* Diff-hl + +Highlight diffs in the fringe. + +#+begin_src emacs-lisp + (use-package diff-hl + :init + (global-diff-hl-mode)) +#+end_src + +* Vertico + +#+begin_src emacs-lisp + (use-package vertico + :init (vertico-mode) + :bind (:map vertico-map + ("<return>" . vertico-directory-enter) + ("<backspace>" . vertico-directory-delete-char) + ("M-<backspace>" . vertico-directory-delete-word))) + + (use-package vertico-prescient + :init (vertico-prescient-mode)) + + (use-package vertico-posframe + :init (vertico-posframe-mode) + :config + (setq vertico-posframe-poshandler 'posframe-poshandler-frame-bottom-center)) +#+end_src + +* Marginalia + +#+begin_src emacs-lisp + (use-package marginalia) +#+end_src + +* Ace Window + +#+begin_src emacs-lisp + (use-package ace-window + :bind (("M-o" . ace-window) + ("C-x o")) + :custom-face + (aw-leading-char-face ((t (:foreground "#222424" :background "#ff9800")))) + :custom + (aw-keys '(?a ?r ?s ?t ?n ?e ?i ?o)) + :config + (defun oni-core-ace-window-select-other-window (window) + "Use ‘ace-window’ to select which window is considered “other”." + (interactive) + (setq other-window-scroll-buffer window)) + + (ace-window-posframe-mode) + (add-to-list 'aw-dispatch-alist '(?O oni-core-ace-window-select-other-window "Select window to be \"other\""))) +#+end_src + +* Rest + +#+begin_src emacs-lisp + (require 'auth-source) + (require 'generic-x) + (require 'package) + (require 'recentf) + (require 'which-key) + + (defconst oni-core--auto-save-directory (oni-core-data-dir "auto-save-files") + "Directory where auto-saves go.") + + (defalias 'yes-or-no-p 'y-or-n-p) + + (defvar oni-core--recentf-idle-timer nil + "Internal variable keeping track of a timer started for ‘recentf-save-list’.") + + (defun oni-core--switch-newline-keys () + "Switch the ‘C-j’ and ‘RET’ keys in the local buffer." + (if electric-indent-mode + (progn + (local-set-key (kbd "C-j") 'newline) + (local-set-key (kbd "RET") 'electric-newline-and-maybe-indent)) + (local-unset-key (kbd "C-j")) + (local-unset-key (kbd "RET")))) + + (defun oni-core--ensure-autosave-directory-exists () + "Create the autosave directory if doesn't exist." + (mkdir oni-core--auto-save-directory t)) + + (defun oni-core-move-beginning-of-dwim () + "Move to beginning of line either after indentation or before." + (interactive) + (let ((start (point))) + (back-to-indentation) + (unless (/= start (point)) + (move-beginning-of-line 1)))) + + (defun oni-core-move-end-of-dwim () + "Move to end of line, either before any comments or after." + (interactive) + (let ((start (point)) + (eolpos (line-end-position))) + (beginning-of-line) + (if (and comment-start + (not (looking-at (regexp-quote comment-start))) + (comment-search-forward eolpos t)) + (condition-case _ + (progn + (search-backward-regexp (concat "[^ \t" comment-start "]")) + (forward-char) + (when (or (bolp) + (= start (point))) + (end-of-line))) + (search-failed (end-of-line))) + (end-of-line)))) + + ;;; From Bastien Guerry’s Emacs configuraiton: + ;;; https://github.com/bzg/dotemacs/blob/master/emacs.org + (defun oni-core-unfill-paragraph () + "Make a multi-line paragraph into a single line of text." + (interactive) + (let ((fill-column (point-max))) + (fill-paragraph nil))) + + (defun oni-core-recentf-save-list-silently () + "Call ‘recentf-save-list’ but without showing a message about it." + (let ((inhibit-message t)) + (recentf-save-list))) + + (defun mutate-number-at-point (mutator) + (let ((bounds (bounds-of-thing-at-point 'symbol))) + (replace-region-contents + (car bounds) + (cdr bounds) + (lambda () + (number-to-string + (funcall mutator (string-to-number + (buffer-substring (point-min) (point-max))))))))) + + (defun increment-number-at-point () + (interactive) + (mutate-number-at-point '1+)) + + (defun decrement-number-at-point () + (interactive) + (mutate-number-at-point '1-)) + + (defhydra oni-sort-and-align-hydra (:color teal :hint nil) + " + ^Sort^ + ^^-------------- + _l_: Lines + _p_: Paragraph + _s_: String list" + ("l" sort-lines) + ("p" (save-excursion + (sort-lines + nil + (progn (start-of-paragraph-text) (point)) + (progn (end-of-paragraph-text) (point))))) + ("s" (sort-regexp-fields + nil + (rx "\"" (one-or-more (not "\"")) "\"") + (rx (one-or-more (not "\""))) + (region-beginning) + (region-end)))) + + (defhydra oni-core-applications-hydra (:color teal :hint nil) + " + ^^^Applications^^^ + ^^--------------------^^-----------------^^-------------- + _m_: Email (notmuch) _r_: RSS (elfeed) _i_: IRC (circe) " + ("m" notmuch) + ("r" elfeed) + ("i" circe)) + + ;;; Get rid of the default help tooltip on the mode-line. + (setq mode-line-default-help-echo nil) + + ;;; Remove the top/bottom/% display of the buffer position. I don't think I've + ;;; ever looked at it. + (setq mode-line-position (cdr mode-line-position)) + + (setq user-full-name "Tom Willemsen" + user-mail-address "tom@punt.com") + + (setq delete-old-versions t) + (setq kept-new-versions 20) + (setq kept-old-versions 20) + (setq vc-make-backup-files t) + (setq version-control t) + (setq require-final-newline t) + (setq sentence-end-double-space nil) + (setq inhibit-startup-screen t) + (setq electric-pair-skip-whitespace 'chomp) + (setq fit-window-to-buffer-horizontally t) + ;; Discovered through + ;; https://github.com/novoid/dot-emacs/blob/23c28944f1991c636ea71ec7d5c3d266e6dbeb8a/config.org#deletes-duplicate-entries-of-the-history-of-the-minibuffer + (setq history-delete-duplicates t) + ;; Save what's in the clipboard into the kill-ring before killing or copying + ;; another string. + (setq save-interprogram-paste-before-kill t) + + ;; Increase the threshold for garbage collection for increased performance. + ;; Apparently there are some (lsp-mode for example) packages that generate a lot + ;; of garbage. + (setq gc-cons-threshold 100000000) + + ;;; Remove ~/.authinfo from the auth sources since it’s an unencripted file and + ;;; I don’t want to accidentally store anything in there. + (setq auth-sources + (cons "secrets:Login" + (remove "~/.netrc" + (remove "~/.authinfo" auth-sources)))) + + (setq read-mail-command 'gnus) + + (setq-default indent-tabs-mode nil) + (setq-default tab-width 4) + (setq-default truncate-lines t) + (setq-default fill-column 80) + + (add-hook 'Info-mode-hook 'mixed-pitch-mode) + (add-hook 'after-save-hook 'executable-make-buffer-file-executable-if-script-p) + (add-hook 'auto-save-hook #'oni-core--ensure-autosave-directory-exists) + (add-hook 'before-save-hook 'time-stamp) + (add-hook 'electric-indent-local-mode-hook #'oni-core--switch-newline-keys) + (add-hook 'prog-mode-hook 'goto-address-prog-mode) + + (global-set-key (kbd "C-M-SPC") 'er/expand-region) + (global-set-key (kbd "C-S-b") 'windmove-left) + (global-set-key (kbd "C-S-f") 'windmove-right) + (global-set-key (kbd "C-S-n") 'windmove-down) + (global-set-key (kbd "C-S-p") 'windmove-up) + (global-set-key (kbd "C-c (") '("Embrace Commander" . embrace-commander)) + (global-set-key (kbd "C-c q") '("Unfill Paragraph" . oni-core-unfill-paragraph)) + (global-set-key (kbd "C-c s") '("Sort and Align Commands" . oni-sort-and-align-hydra/body)) + (global-set-key (kbd "C-c a") '("Applications" . oni-core-applications-hydra/body)) + (global-set-key (kbd "C-c l") 'imenu) + (global-set-key (kbd "C-x C-b") '("List Buffers" . ibuffer-jump)) + (global-set-key (kbd "C-x M-f") 'ffap) + (global-set-key (kbd "M-+") 'mc/mark-next-like-this) + (global-set-key [remap insert-char] 'insert-char-preview) + (global-set-key [remap move-beginning-of-line] #'oni-core-move-beginning-of-dwim) + (global-set-key [remap move-end-of-line] #'oni-core-move-end-of-dwim) + (global-set-key [remap upcase-word] #'upcase-dwim) + (global-set-key [remap downcase-word] #'downcase-dwim) + (global-set-key [remap capitalize-word] #'capitalize-dwim) + (global-set-key (kbd "C-c i +") #'increment-number-at-point) + (global-set-key (kbd "C-c i -") #'decrement-number-at-point) + + (global-set-key (kbd "C-<left>") 'winner-undo) + (global-set-key (kbd "C-<right>") 'winner-redo) + + (unless oni-core--recentf-idle-timer + (setq oni-core--recentf-idle-timer + (run-with-idle-timer 10 t #'oni-core-recentf-save-list-silently))) + + (with-eval-after-load 'which-key (diminish 'which-key-mode)) + + (add-to-list 'display-buffer-alist + `(,(rx string-start + "*" (any ?h ?H) "elp") + display-buffer-in-side-window + (side . bottom) + (slot . 0) + (window-height . 0.33))) + + (when (eql system-type 'windows-nt) + (add-to-list 'load-path (locate-user-emacs-file "vendor/p4-vc")) + (add-to-list 'exec-path "c:/Program Files/Git/bin") + (add-to-list 'exec-path "C:/Program Files/Git/usr/bin") + (add-to-list 'exec-path "c:/cygwin64/bin" t) + + (setq delete-by-moving-to-trash t) + + (setq-default buffer-file-coding-system 'utf-8-unix)) + + ;; Set up autoloads for all of my configuration files so that when they get + ;; installed they get loaded automatically, but don’t require them to be + ;; installed. + + ;; (with-eval-after-load 'ahk-mode (require 'oni-autohotkey nil t)) + ;; (with-eval-after-load 'alert (require 'oni-alert nil t)) + ;; (with-eval-after-load 'bat-mode (require 'oni-bat nil t)) + ;; (with-eval-after-load 'bats (require 'oni-bats nil t)) + ;; (with-eval-after-load 'bookmark (require 'oni-bookmark nil t)) + ;; (with-eval-after-load 'browse-url (require 'oni-browse-url nil t)) + ;; (with-eval-after-load 'cc-mode (require 'oni-c nil t)) + ;; (with-eval-after-load 'cc-mode (require 'oni-cpp nil t)) + ;; (with-eval-after-load 'cc-mode (require 'oni-java nil t)) + ;; (with-eval-after-load 'cider (require 'oni-clojure nil t)) + ;; (with-eval-after-load 'circe (require 'oni-circe nil t)) + ;; (with-eval-after-load 'clojure-mode (require 'oni-clojure nil t)) + ;; (with-eval-after-load 'cmake-mode (require 'oni-cmake nil t)) + ;; (with-eval-after-load 'company (require 'oni-company nil t)) + ;; (with-eval-after-load 'compile (require 'oni-compilation nil t)) + ;; (with-eval-after-load 'conf-mode (require 'oni-conf nil t)) + ;; (with-eval-after-load 'counsel (require 'oni-counsel nil t)) + ;; (with-eval-after-load 'csharp-mode (require 'oni-csharp nil t)) + ;; (with-eval-after-load 'css-mode (require 'oni-css nil t)) + ;; (with-eval-after-load 'diff-hl (require 'oni-diff-hl nil t)) + ;; (with-eval-after-load 'dired (require 'oni-dired nil t)) + ;; (with-eval-after-load 'ediff (require 'oni-ediff nil t)) + ;; (with-eval-after-load 'elfeed (require 'oni-elfeed nil t)) + ;; (with-eval-after-load 'elisp-mode (require 'oni-elisp nil t)) + ;; (with-eval-after-load 'elm-mode (require 'oni-elm nil t)) + ;; (with-eval-after-load 'embrace (require 'oni-embrace nil t)) + ;; (with-eval-after-load 'emms (require 'oni-emms nil t)) + ;; (with-eval-after-load 'eshell (require 'oni-eshell nil t)) + ;; (with-eval-after-load 'fish-mode (require 'oni-fish nil t)) + ;; (with-eval-after-load 'flycheck (require 'oni-flycheck nil t)) + ;; (with-eval-after-load 'git-commit (require 'oni-git-commit nil t)) + ;; (with-eval-after-load 'gnus (require 'oni-gnus nil t)) + ;; (with-eval-after-load 'grep (require 'oni-grep nil t)) + ;; (with-eval-after-load 'groovy-mode (require 'oni-groovy nil t)) + ;; (with-eval-after-load 'haskell-mode (require 'oni-haskell nil t)) + ;; (with-eval-after-load 'highlight-indent-guides (require 'oni-highlight-indent-guides nil t)) + ;; (with-eval-after-load 'hydra (require 'oni-hydra nil t)) + ;; (with-eval-after-load 'ielm (require 'oni-elisp nil t)) + ;; ;; (with-eval-after-load 'ivy (require 'oni-ivy)) + ;; (with-eval-after-load 'jabber (require 'oni-jabber nil t)) + ;; (with-eval-after-load 'js2-mode (require 'oni-js nil t)) + ;; (with-eval-after-load 'json-mode (require 'oni-json nil t)) + ;; (with-eval-after-load 'lisp-mode (require 'oni-common-lisp nil t)) + ;; (with-eval-after-load 'log-edit (require 'oni-log-edit nil t)) + ;; (with-eval-after-load 'lsp (require 'oni-lsp nil t)) + ;; (with-eval-after-load 'lua-mode (require 'oni-lua nil t)) + ;; (with-eval-after-load 'lui (require 'oni-lui nil t)) + ;; (with-eval-after-load 'magit (require 'oni-magit nil t)) + ;; (with-eval-after-load 'make-mode (require 'oni-makefile nil t)) + ;; (with-eval-after-load 'notmuch (require 'oni-notmuch nil t)) + ;; (with-eval-after-load 'nov (require 'oni-epub nil t)) + ;; (with-eval-after-load 'org (require 'oni-org nil t)) + ;; (with-eval-after-load 'org-roam (require 'oni-org-roam nil t)) + ;; (with-eval-after-load 'package (require 'oni-package nil t)) + ;; (with-eval-after-load 'package-x (require 'oni-package nil t)) + ;; (with-eval-after-load 'paredit (require 'oni-paredit nil t)) + ;; (with-eval-after-load 'php-mode (require 'oni-php nil t)) + ;; (with-eval-after-load 'prescient (require 'oni-prescient nil t)) + ;; (with-eval-after-load 'projectile (require 'oni-projectile nil t)) + ;; (with-eval-after-load 'python (require 'oni-python nil t)) + ;; (with-eval-after-load 'ruby-mode (require 'oni-ruby nil t)) + ;; (with-eval-after-load 'rust-mode (require 'oni-rust nil t)) + ;; (with-eval-after-load 'scheme (require 'oni-scheme nil t)) + ;; (with-eval-after-load 'sgml-mode (require 'oni-html nil t)) + ;; (with-eval-after-load 'sh (require 'oni-sh nil t)) + ;; (with-eval-after-load 'shr (require 'oni-shr nil t)) + ;; (with-eval-after-load 'smartparens (require 'oni-smartparens nil t)) + ;; (with-eval-after-load 'sort (require 'oni-sort nil t)) + ;; (with-eval-after-load 'tramp (require 'oni-tramp nil t)) + ;; (with-eval-after-load 'web-mode (require 'oni-web nil t)) + + (eval-when-compile + (require 'calendar)) + + (with-eval-after-load 'calendar + ;; I’m used to working with Monday as the starting day. + (setq calendar-week-start-day 1 + calendar-date-style 'iso)) + + (eval-when-compile (require 'solar)) + (with-eval-after-load 'solar + (setq calendar-latitude 49.2127205 + calendar-longitude -122.9267927)) + + (defun oni-core-in-word-p () + "Check whether the character just typed was part of a word." + (save-excursion + (backward-char) + (looking-back (rx word) (1- (point))))) + + (defun oni-core-ace-window-select-other-window () + "Use ‘ace-window’ to select which window is considered “other”." + (interactive) + (setq other-window-scroll-buffer (ace-select-window))) + + (with-eval-after-load 'electric + (add-hook 'electric-quote-inhibit-functions #'oni-core-in-word-p)) + + (eval-when-compile (require 'ispell)) + (with-eval-after-load 'ispell + (setq ispell-program-name "hunspell" + ispell-really-hunspell t)) + + ;; Enable any modes that I want to have turned on right away. + + (electric-indent-mode -1) + (winner-mode) + (recentf-mode) + (which-key-mode) + (auto-insert-mode) + ;;; Remember minibuffer history across sessions. + (savehist-mode) + ;;; Remember the place I left at in files when closing them. + (save-place-mode) + ;;; Mitigate performance issues with files with _really_ long lines. + (global-so-long-mode) + + ;;; Minibuffer + + (add-hook 'minibuffer-setup-hook 'electric-pair-local-mode) + (add-hook 'minibuffer-setup-hook 'cursor-intangible-mode) + + (setq minibuffer-prompt-properties + (append '(cursor-intangible t) minibuffer-prompt-properties)) + + (setq read-extended-command-predicate 'command-completion-default-include-p) + (setq read-buffer-completion-ignore-case t) + (setq read-file-name-completion-ignore-case t) + (setq completion-ignore-case t) + + ;;; Vertico + + (vertico-mode) + (vertico-prescient-mode) + + (define-key vertico-map (kbd "<return>") 'vertico-directory-enter) + (define-key vertico-map (kbd "<backspace>") 'vertico-directory-delete-char) + (define-key vertico-map (kbd "M-<backspace>") 'vertico-directory-delete-word) + + ;;; Marginalia + + (define-key minibuffer-local-map (kbd "M-A") 'marginalia-cycle) + + (marginalia-mode) + + (global-set-key (kbd "C-c w d") '("Delete a window" . ace-delete-window)) + (global-set-key (kbd "C-c w k") '("Keep a single window" . ace-delete-other-windows)) + (global-set-key (kbd "C-c w o") '("Select other window" . oni-core-ace-window-select-other-window)) + + (global-set-key (kbd "C-x 8 l") "λ") + + (defun oni-core-related-files () + "Return a list of files related to the current buffer." + (let* ((jumpers related-files-jumpers) + (current-place (buffer-file-name))) + (cond ((not jumpers) + (user-error "No jumpers. Consider configuring `related-files-jumpers'")) + ((not current-place) + (user-error "Related-Files only works from file-based buffers")) + (t + (related-files--collect-existing-places jumpers (list current-place)))))) + + (defvar oni-core-related-places-source + '(:name "Related File" + :category file + :items oni-core-related-files + :enabled buffer-file-name + :action find-file)) + + (eval-when-compile + (require 'consult)) + + (with-eval-after-load 'consult + (with-eval-after-load 'related-files + (add-to-list 'consult-buffer-sources 'oni-core-related-places-source)) + + (consult-customize consult-buffer consult-bookmark :preview-key nil)) + + (setq xref-show-definitions-function 'consult-xref) + + ;;; Embark + + (global-set-key (kbd "C-.") 'embark-act) + (global-set-key (kbd "C-;") 'embark-dwim) + (global-set-key [remap describe-bindings] 'embark-bindings) + + (defun oni-core-fit-window-to-buffer (window) + (fit-window-to-buffer window) + (with-selected-window window + (enlarge-window-horizontally 1))) + + (add-to-list 'display-buffer-alist + `(,(rx string-start " *Embark Actions*") + display-buffer-in-side-window + (window-parameter (mode-line-format . none)) + (side . right) + (slot . 0) + (window-width . oni-core-fit-window-to-buffer))) + + ;;; Extra debugging + + ;;; From https://gist.github.com/jdtsmith/1fbcacfe677d74bbe510aec80ac0050c + (defun oni-core-reraise-error (func &rest args) + "Call function FUNC with ARGS and re-raise any error which occurs. + Useful for debugging post-command hooks and filter functions, + which normally have their errors suppressed." + (condition-case err + (apply func args) + ((debug error) (signal (car err) (cdr err))))) + + (defun toggle-debug-on-hidden-errors (func) + "Toggle hidden error debugging for function FUNC." + (interactive "a") + (cond + ((advice-member-p #'oni-core-reraise-error func) + (advice-remove func #'oni-core-reraise-error) + (message "Debug on hidden errors disabled for %s" func)) + (t + (advice-add func :around #'oni-core-reraise-error) + (message "Debug on hidden errors enabled for %s" func)))) + + ;;; Native Compilation + + (setq native-comp-async-report-warnings-errors 'silent) + + ;;; Lazy counting + + (with-eval-after-load 'isearch + (setq isearch-lazy-count t)) + + ;;; Tree sitter + + (when (treesit-available-p) + (add-to-list 'interpreter-mode-alist '("bash" . bash-ts-mode))) + + (defun oni-core-copy-guix-build-hash () + "Try and find the last actual hash in the compilation buffer and insert it." + (interactive) + (let* ((hash (with-current-buffer next-error-last-buffer + (save-excursion + (save-match-data + (goto-char (point-max)) + (re-search-backward (rx bol (zero-or-more whitespace) "actual hash:" (one-or-more whitespace))) + (goto-char (match-end 0)) + (buffer-substring-no-properties (point) (line-end-position)))))) + (struct (let ((obj (save-excursion + (beginning-of-defun) + (read (current-buffer))))) + (setf (cadar (map-elt (cdar (map-elt (cdadr (cdaddr obj)) 'source)) 'sha256)) + hash) + obj))) + (beginning-of-defun) + (let ((start (point))) + (forward-sexp) + (delete-region start (point)) + (insert (pp struct)) + (indent-region start (point)) + (skip-syntax-forward "> ") + (ensure-empty-lines)))) + + (defun oni-core--without-dangerous-directories (directory) + "Call ‘directory-files' on DIRECTORY, but remove certain directories." + (cl-delete-if + (lambda (d) (string-match-p (rx "/" (or "." ".." "foreign" ".git") eos) d)) + (directory-files directory t))) + + (if (boundp 'safe-local-variable-directories) + (setq safe-local-variable-directories + (apply 'append (mapcar + (lambda (d) + (oni-core--without-dangerous-directories d)) + (oni-core--without-dangerous-directories "~/code/"))))) + + ;;; Space cycling + + ;; The ‘-’ argument to each function makes sure that any newlines are also + ;; removed along with the other whitespace. + (setq cycle-spacing-actions '((just-one-space -) (delete-all-space -) restore)) + + ;;; Colorize the unique part of buffers + + (defun oni-core-colored-post-forward-angle-brackets (base extra-strings) + "Return BASE<EXTRA-STRINGS> with the second part colorized. + This does the same thing as setting ‘uniquify-buffer-name-style' to + ‘post-forward-angle-brackets', except that it colorizes the unique part + of the buffer name." + (concat base + (propertize (concat "<" (mapconcat #'identity extra-strings "/") ">") + 'face '(:foreground "#72f1f1")))) + + (setq uniquify-buffer-name-style 'oni-core-colored-post-forward-angle-brackets) + + ;;; Make switching buffers obey ‘display-buffer-alist’ + + (setq switch-to-buffer-obey-display-actions t) +#+end_src + +* Rainbow Delimiters + +#+begin_src emacs-lisp + (use-package rainbow-delimiters + :hook (php-mode emacs-lisp-mode)) +#+end_src + +* Mixed Pitch Mode + +#+begin_src emacs-lisp + (use-package mixed-pitch) +#+end_src + +* Fennel + +#+begin_src emacs-lisp + (use-package fennel-mode) +#+end_src + +* Custom + +Change the file where =customize= stores its settings. + +#+begin_src emacs-lisp + (setq custom-file (expand-file-name "custom.el" user-emacs-directory)) + (load custom-file) +#+end_src + +* Orderless + +#+begin_src emacs-lisp + (use-package orderless + :init + (setq completion-styles '(basic partial-completion orderless)) + :config + (add-to-list 'orderless-matching-styles 'orderless-initialism)) +#+end_src + +* Org-Caldav + +This allows me to synchronize tasks and events between my phone and Emacs. + +#+begin_src emacs-lisp + (use-package org-caldav + :config + (setq org-caldav-url "https://phoenix.fold-kanyu.ts.net/" + org-caldav-calendar-id "tom/196e7d47-b820-a12a-c902-113871fcade7" + org-caldav-inbox "~/documents/personal.org" + org-caldav-files nil + org-icalendar-timezone "America/Vancouver" + org-icalendar-include-todo 'all + org-caldav-sync-todo t + org-caldav-todo-percent-states '((0 "TODO") (1 "NEXT") (100 "DONE")))) +#+end_src + +* Wakatime + +I run my own [[https://wakapi.dev/][Wakapi]] instance to track my coding time. + +#+begin_src emacs-lisp + (use-package wakatime-mode + :init + (global-wakatime-mode) + :custom + (wakatime-cli-path "/opt/homebrew/bin/wakatime-cli")) +#+end_src + +* Directory-locals + +This is the definition of directory-local variables for the new Punt front-end. + +#+begin_src emacs-lisp + (use-package files + :ensure nil + :config + (dir-locals-set-class-variables + 'sweepstakes-frontend + '((nil . ((grep-find-ignored-directories + . ("apps/*/.next" "node_modules" ".pnpm-store")))))) + + (dir-locals-set-directory-class + (expand-file-name "~/code/diamond-interactive/sweepstakes-frontend") + 'sweepstakes-frontend)) +#+end_src + +Now the pull app. + +#+begin_src emacs-lisp + (use-package files + :ensure nil + :config + (dir-locals-set-class-variables + 'pull-app + '((nil . ((grep-find-ignored-directories + . ("node_modules")))))) + + (dir-locals-set-directory-class + (expand-file-name "~/code/diamond-interactive/pull-app") + 'pull-app)) +#+end_src + +* Eglot + +Set up a server program for Eglot for org-mode. + +#+begin_src emacs-lisp + (use-package eglot + :if (eq system-type 'darwin) + :ensure-system-package (harper-ls . harper) + :config + (add-to-list 'eglot-server-programs + '(org-mode . ("harper-ls" "--stdio")))) +#+end_src + +* Minions + +Hide all of my minor modes in a single menu item. + +#+begin_src emacs-lisp + (use-package minions + :init + (minions-mode)) +#+end_src + +* Corfu + +This is a modern in-buffer completion framework that works a little more nicely with internal existing tools in Emacs than Company does. I've used company for years and it was a big improvement over the previous autocomplete, but now I'm trying this out. + +This includes cape for extra completion-at-point functions, and corfu-prescient to show the selected option in the buffer. + +#+begin_src emacs-lisp + (use-package corfu + :config + (setq corfu-auto t)) + + (use-package cape + :init + (add-to-list 'completion-at-point-functions 'cape-abbrev) + (add-to-list 'completion-at-point-functions 'cape-dabbrev) + (add-to-list 'completion-at-point-functions 'cape-file)) + + (use-package corfu-prescient + :hook (corfu-mode)) +#+end_src + +* Golden Ratio + +This package automatically adjusts your window sized to follow the golden ratio, so that the focused one is always the biggest. + +#+begin_src emacs-lisp + (use-package golden-ratio + :init + (golden-ratio-mode) + :config + (with-eval-after-load 'ace-window + (advice-add 'ace-window :after #'golden-ratio)) + (add-to-list 'golden-ratio-exclude-modes 'calculator-mode)) +#+end_src + +This configuration adds advice after the =ace-window= command to make sure that switching with it still properly configures the window sizes. + +* Phel + +Phel is a Clojure-inspired lisp that compiles to PHP. + +#+begin_src emacs-lisp + (use-package phel-mode + :mode (rx ".phel" eos)) +#+end_src |
