GNU Emacs Configuration
I’ve recently moved from a literate Emacs configuration based on Org
mode to a simpler init.el
file, reproduced below:
;;; init.el --- Personal Emacs configuration -*- lexical-binding: t -*- ;; Copyright (C) 2021-2023 Eshel Yaron ;; Author: Eshel Yaron <[email protected]> ;;; Commentary: ;; My personal Emacs configuration ;;; Code: (defvar esy/init-step-times nil) (defmacro esy/init-step (name _doc &rest body) (declare (indent defun) (doc-string 2)) (let ((start-time-symbol (gensym))) `(let ((,start-time-symbol (current-time))) ,@body (push (cons ',name (time-subtract (current-time) ,start-time-symbol)) esy/init-step-times)))) (esy/init-step gc "Temporarily increase GC threshold to expedite Emacs startup." (let ((normal-gc-cons-threshold (* 20 1024 1024)) (init-gc-cons-threshold (* 1024 1024 1024)) (normal-mode-line-format mode-line-format)) (setq gc-cons-threshold init-gc-cons-threshold mode-line-format nil) (add-hook 'after-init-hook (lambda () (setq gc-cons-threshold normal-gc-cons-threshold mode-line-format normal-mode-line-format))))) (esy/init-step vars "Set some variables." (setq ;; my name user-full-name "Eshel Yaron" ;; my email address user-mail-address "[email protected]" ;; direct Custom definitions to some file and forget about it custom-file (expand-file-name "custom.el" user-emacs-directory) ;; silence native compilation warnings native-comp-async-report-warnings-errors 'silent ;; only display the warning buffer if something's really broken warning-minimum-level :error ;; don't use stale .elc files load-prefer-newer t ;; maximize Emacs on startup default-frame-alist '((fullscreen . fullboth)) ;; disable popup dialogs use-dialog-box nil ;; disable splash screen inhibit-startup-screen t ;; make the scratch message more concise initial-scratch-message ";; Go.\n" ;; don't ring the bell ring-bell-function #'ignore ;; make C-x b obey display-buffer-alist switch-to-buffer-obey-display-actions t ;; disable new mail mode line indication display-time-mail-function #'ignore ;; enable recursive minibuffers enable-recursive-minibuffers t ;; save bookmarks immediately bookmark-save-flag 1 ;; show matches count in isearch prompt isearch-lazy-count t ;; make some space in the Packages menu package-archive-column-width 12 package-version-column-width 28 ;; configure package archives package-archives '(("melpa" . "http://melpa.org/packages/") ("gnu" . "https://elpa.gnu.org/devel/") ("nongnu" . "https://elpa.nongnu.org/nongnu-devel/")) ;; select some packages to install package-selected-packages '( all-the-icons all-the-icons-completion all-the-icons-dired all-the-icons-gnus auctex avy bbdb corfu devdocs diff-hl elfeed embark-consult emms gnu-elpa-keyring-update gnuplot graphql-mode graphviz-dot-mode htmlize ialign keycast kubernetes lin magit marginalia markdown-mode mastodon ob-prolog orderless org org-transclusion package-lint paredit pdf-tools rainbow-delimiters rainbow-mode rg smtpmail-multi sqlformat sweeprolog terraform-mode vterm vundo whitespace-cleanup-mode ) ;; configure Org capture templates org-capture-templates '(("t" "Todo [inbox]" entry (file+headline "~/org/inbox.org" "Tasks") "** TODO [#B] %^{Task} %^g :PROPERTIES: :CreatedAt: %u :CapturedAt: %a :CapturedAs: Inbox Task :END:" :prepend t :empty-lines 1 :immediate-finish t) ("w" "Work [inbox]" entry (file+headline "~/org/inbox.org" "Tasks") "** TODO [#B] %^{Task} :work: :PROPERTIES: :CreatedAt: %u :CapturedAt: %a :CapturedAs: Work Task :END:" :prepend t :empty-lines 1 :immediate-finish t) ("c" "New Calendar Event" entry (file+headline "~/org/inbox.org" "Calendar") "** %^{Title} %^g :PROPERTIES: :CreatedAt: %u :CapturedAt: %a :CapturedAs: Calendar Event :END: %(format-time-string \"<%Y-%m-%d %H:%M\" (org-read-date t t))-%(format-time-string \"%H:%M>\" (org-read-date t t)) %i" :prepend t :empty-lines 1 :immediate-finish t)) ;; point Org to file agenda file org-agenda-files '("~/org/inbox.org") org-default-notes-file "~/org/inbox.org" ;; weeks start on Sunday org-agenda-start-on-weekday 0 ;; use a nice unicode ellipsis org-ellipsis "…" ;; open everything withing Emacs org-file-apps '((t . emacs)) ;; enable Org speed commands org-use-speed-commands t ;; task states org-todo-keywords '((sequence "TODO(t)" "BLOCKED(b@/!)" "INPROGRESS(i!)" "|" "DONE(d!)" "CANCELED(c@)")) ;; set a task to INPROGRESS when I clock into it org-clock-in-switch-to-state "INPROGRESS" ;; log the finish time of tasks in Org org-log-done 'time org-log-into-drawer t ;; minimal prompt for task states org-use-fast-todo-selection 'expert ;; set up a group of mutual exclusive tags org-tag-alist '((:startgroup) ("adi" . ?a) ("holland" . ?h) ("work" . ?w) ("studies" . ?s) ("esols" . ?e) ("personal" . ?p) (:endgroup)) ;; minimal prompt for tags org-fast-tag-selection-single-key 'expert ;; allow evaluating some languages with Org Babel org-babel-load-languages '((emacs-lisp . t) (shell . t) (sql . t) (prolog . t)) ;; do not ask for confirmation when evaluating Org source blocks org-confirm-babel-evaluate nil ;; extra modules to load along with Org org-modules '(ol-bbdb ol-bibtex ol-docview ol-gnus ol-info ol-irc ol-mhe ol-eww ob-sql org-tempo) ;; refile Org entries to my agenda file(s) org-refile-targets '((org-agenda-files . (:maxlevel . 5)) (nil . (:maxlevel . 3))) ;; use full outline path when prompting for refile target org-refile-use-outline-path t ;; archive for Org entries org-archive-location "~/org/journal.org::datetree/* Finished Tasks :ARCHIVE:" ;; associate Org Babel languages with major modes org-src-lang-modes '(("SWI-Prolog" . sweeprolog) ("Dockerfile" . dockerfile-ts) ("bash" . sh) ("shell" . sh) ("toml" . toml-ts)) ;; don't auto-save remote files remote-file-name-inhibit-auto-save t ;; don't ask me about it either tramp-allow-unsafe-temporary-files t ;; increase maximum number of recent files Emacs remembers recentf-max-saved-items 128 ;; increase maximum kill ring size kill-ring-max 256 ;; save text copied from another program to the kill ring save-interprogram-paste-before-kill 2048 ;; have C-u followed by repeated C-SPC keep popping set-mark-command-repeat-pop t ;; enable italic text in code modus-themes-italic-constructs t ;; enable bold text in code modus-themes-bold-constructs t ;; use fixed-pitch text where appropriate modus-themes-mixed-fonts t ;; make prompts bold modus-themes-prompts '(bold) ;; give source blocks a distinct background shade modus-themes-org-blocks 'gray-background ;; use variable-pitch text here are there modus-themes-variable-pitch-ui t ;; fine-tune some theme settings modus-themes-common-palette-overrides '((border-mode-line-active unspecified) (border-mode-line-inactive unspecified) (bg-mode-line-active bg-blue-intense) (fg-mode-line-active fg-main) (fringe unspecified) (underline-link border) (underline-link-visited border) (underline-link-symbolic border) (fg-region unspecified)) ;; follow links to version-controlled files without confirming vc-follow-symlinks t ;; jump from .c to .h files with find-sibling-file find-sibling-rules '(("\\([^/]+\\)\\.c\\'" "\\1.h")) ;; configure multiple SMTP accounts smtpmail-multi-accounts '((esy . ("[email protected]" "smtp.gmail.com" 587 "[email protected]" starttls nil nil nil)) (swp . ("[email protected]" "mail.swi-prolog.com" 587 "[email protected]" starttls nil nil nil)) (me . ("[email protected]" "mail.eshelyaron.com" 587 "[email protected]" starttls nil nil nil)) ;; (uva . ("[email protected]" ;; "smtp.office365.com" ;; 587 ;; "[email protected]" ;; starttls nil nil nil)) ) ;; associate email addresses with SMTP accounts smtpmail-multi-associations '(("eshelshay\[email protected]\.com" esy) ("[email protected]\.org" swp) ("[email protected]\.com" me) ;; ("eshel\[email protected]\.uva\.nl" uva) ) ;; set function for sending email send-mail-function #'smtpmail-multi-send-it message-send-mail-function #'smtpmail-multi-send-it ;; set email user agent mail-user-agent 'gnus-user-agent read-mail-command 'gnus ;; make Gnus not prompt as much gnus-expert-user t ;; make Gnus read the dribble file on startup without prompting gnus-always-read-dribble-file t ;; don't break pages in incoming email gnus-break-pages nil ;; don't show the Gnus startup screen gnus-inhibit-startup-message t ;; set Gnus backends gnus-select-method '(nntp "news.gmane.io") gnus-secondary-select-methods '((nnimap "gmail" (nnimap-address "imap.gmail.com") (nnimap-server-port "imaps") (nnimap-stream ssl)) (nnimap "me" (nnimap-address "mail.eshelyaron.com") (nnimap-server-port "imaps") (nnimap-stream ssl)) (nnimap "swi" (nnimap-address "mail.swi-prolog.com") (nnimap-server-port "imaps") (nnimap-stream ssl)) ;; (nnimap "uva" ;; (nnimap-address "outlook.office365.com") ;; (nnimap-server-port "imaps") ;; (nnimap-stream ssl)) ) ;; simpler message gnus-no-groups-message "No new articles" ;; don't close other windows when opening Gnus gnus-use-full-window nil ;; make Gnus handle this content types gnus-article-treat-types '("text/plain" "text/x-verbatim" "text/x-patch" "text/html" "text/calendar") ;; capture calendar events from Gnus to my Org inbox file gnus-icalendar-org-capture-file "~/org/inbox.org" ;; put calendar events under the Calendar heading gnus-icalendar-org-capture-headline '("Calendar") ;; use ISO format for calendar dates calendar-date-style 'iso ;; maintain legibility of rendered text in HTML mails shr-color-visible-luminance-min 75 ;; my Mastodon settings mastodon-instance-url "https://emacs.ch" mastodon-active-user "eshel" ;; direct Magit to my Git checkouts directory magit-repository-directories '(("~/checkouts/" . 1)) ;; have Dired operations target another visible Dired buffer dired-dwim-target t ;; use zsh in vterm by default vterm-shell "/bin/zsh" vterm-max-scrollback 2048 vterm-kill-buffer-on-exit nil vterm-use-vterm-prompt-detection-method t ;; use MPV with EMMS emms-player-list '(emms-player-mpv) ;; free-style numbering plan bbdb-phone-style nil ;; allow cycling through mail address completion candidate bbdb-complete-mail-allow-cycling t ;; don't pop up BBDB records after completing mail addresses bbdb-completion-display-record nil eww-auto-rename-buffer 'title browse-url-browser-function #'eww-browse-url browse-url-generic-program "open" global-auto-revert-non-file-buffers t auto-revert-verbose nil query-about-changed-file t ;; show flymake diagnostics as overlays at eol ;; flymake-show-diagnostics-at-end-of-line t kill-do-not-save-duplicates t show-trailing-whitespace t read-extended-command-predicate #'command-completion-default-include-p completions-format 'one-column completion-auto-select nil completion-styles '(orderless partial-completion basic) completion-show-help nil ;; completions-header-format nil completion-auto-help 'visual completions-max-height 16 completion-auto-wrap t corfu-cycle t corfu-indexed-start 1 shell-kill-buffer-on-exit t ;; allow disabling confirming before compilation via local variables safe-local-variable-values '((compilation-read-command . nil)) TeX-view-program-selection '((output-pdf "PDF Tools")) TeX-source-correlate-start-server t xref-show-definitions-function #'consult-xref xref-show-xrefs-function #'consult-xref xref-search-program 'ripgrep ;; include CWD in shell command prompts shell-command-prompt-show-cwd t sqlformat-command 'pgformatter sql-input-ring-file-name (expand-file-name ".sqli-history" user-emacs-directory) ;; use relative line numbers display-line-numbers-type 'relative ;; persist Git commit message history savehist-additional-variables '(log-edit-comment-ring) ;; IRC stuff rcirc-default-nick "esy" rcirc-server-alist '(("irc.libera.chat" :channels ("#emacs") :port 6697 :encryption tls)) rcirc-log-flag t ;; use my custom project-prompting function project-prompter #'esy/read-project-by-name ;; have common bindings initially hidden in the output of C-h b describe-bindings-outline-rules `((match-regexp . ,(regexp-opt '("Key translations" "Global Bindings:" "Function key map translations" "pixel-scroll-precision-mode" "context-menu-mode"))))) (setq-default indent-tabs-mode nil)) (esy/init-step theme "Load the `modus-vivendi' theme." (require-theme 'modus-themes) (load-theme 'modus-vivendi)) (esy/init-step fonts "Set up the Iosevka font family." (set-face-attribute 'default nil :family "Iosevka" :height 130) (set-face-attribute 'fixed-pitch nil :family "Iosevka") (set-face-attribute 'variable-pitch nil :family "Iosevka Etoile")) (esy/init-step load-path "Add custom code directory to `load-path'." (add-to-list 'load-path (expand-file-name "lisp/" user-emacs-directory)) (autoload 'some-button "some-button" nil t) (autoload 'esy-capture "esy-capture" nil t) (autoload 'pdf-view-mode "pdf-view" nil t)) (esy/init-step commands "Define custom commands." (defvar-keymap transpose-lines-repeat-map :doc "Repeat map for \\[transpose-lines]" "C-t" #'transpose-lines) (put 'transpose-lines 'repeat-map 'transpose-lines-repeat-map) ;; utility commands (defun esy/kill-dwim () "When region is active, kill region, otherwise kill last word." (interactive) (if (region-active-p) (let ((beg (region-beginning)) (end (region-end))) (kill-region beg end)) (let ((end (point))) (backward-word) (kill-region (point) end)))) (defun esy/pulse-line (&optional _) "Pulse current line." (interactive) (pulse-momentary-highlight-one-line)) (defun esy/seconds-to-date-string (seconds) "Decode and display the date corresponding to SECONDS." (interactive (list (if (use-region-p) (string-to-number (buffer-substring (region-beginning) (region-end))) (read-number "Unix timestamp: " (round (float-time)))))) (message (format-time-string "%FT%T%z" (seconds-to-time seconds)))) (defun esy/find-init-file (init) "Find the Emacs INIT file." (interactive (list user-init-file)) (find-file init)) (defun esy/eww-target () (require 'eww) (if (use-region-p) (let ((target (buffer-substring-no-properties (use-region-beginning) (use-region-end)))) (add-to-history 'eww-prompt-history target) target) (completing-read "Browse or search: " eww-prompt-history nil nil nil 'eww-prompt-history (eww-suggested-uris)))) (defun esy/eww (target) "Browse or search for TARGET." (interactive (list (esy/eww-target))) (eww target)) (with-eval-after-load 'shell (keymap-set shell-mode-map "SPC" #'comint-magic-space)) (defvar-local esy/log-buffer-filename nil "File in which the current buffer is logged.") (defun esy/log-buffer () "Save the current buffer under the ~/logs/ directory." (interactive) (let ((filename (or esy/log-buffer-filename (setq esy/log-buffer-filename (expand-file-name (concat mode-name "_" (format-time-string "%Y%m%d%H%M%S" (current-time)) ".log") "~/logs"))))) (save-restriction (widen) (write-region (point-min) (point-max) filename)))) (with-eval-after-load 'comint (keymap-set comint-mode-map "C-c C-q" #'esy/log-buffer)) (defun esy/recent-log-summary () "Display a summary of my website's most recent access log." (interactive) (let ((default-directory "/ssh:[email protected]:/var/log/apache2/")) (async-shell-command (string-join '("grep -E '\"GET.+\" 2' access.log" "tr -d '\"'" "tr -d \"'\"" "cut -f 7,11,12 -d ' '" "sort" "uniq -c" "sort -n") " | ") "*Recent Log Summary*"))) (defun esy/access-log-summary () "Display a summary of my website's access log." (interactive) (let ((default-directory "/ssh:[email protected]:/var/log/apache2/")) (async-shell-command (string-join '("zcat access.log.*.gz" "cat - access.log access.log.1" "grep -E '\"GET.+\" 2'" "tr -d '\"'" "tr -d \"'\"" "cut -f 7,11,12 -d ' '" "sort" "uniq -c" "sort -n") " | ") "*Access Log Summary*"))) (defun esy/clone (remote) (interactive (list (let ((default (thing-at-point 'url))) (read-string (format-prompt "Clone" default) nil nil default)))) (let ((dir (expand-file-name (file-name-base (car (url-path-and-query (url-generic-parse-url remote)))) "~/checkouts"))) (vc-clone remote 'Git dir) (find-file dir)))) (esy/init-step auto-exec-permissions "Ensure scripts ran with `executable-interpret' are executable." (with-eval-after-load 'executable (define-advice executable-interpret (:before (&rest _) ensure-executable) (unless (file-exists-p buffer-file-name) (basic-save-buffer)) (executable-make-buffer-file-executable-if-script-p)))) (esy/init-step pulse-on-window-selection-change "Pulse the line around point after switching windows." (defun esy/pulse-on-window-selection-change (&rest _) (pulse-momentary-highlight-one-line)) (add-hook 'window-selection-change-functions #'esy/pulse-on-window-selection-change)) (esy/init-step startup-message "Override the default startup message." (define-advice startup-echo-area-message (:override () report-init-time) (format "%s started in %s. Hack away." (propertize "Emacs" 'face 'success) (propertize (emacs-init-time) 'face 'error )))) (esy/init-step packages "Ensure external packages are installed." (package-install-selected-packages)) (esy/init-step prog-mode-hook "Extend standard programming mode hooks." (dolist (mode '(bug-reference-prog-mode display-fill-column-indicator-mode display-line-numbers-mode flymake-mode flyspell-prog-mode rainbow-delimiters-mode)) (add-hook 'prog-mode-hook mode)) (add-hook 'lisp-data-mode-hook #'paredit-mode)) (esy/init-step other-hooks "Extend other standard hooks." (add-hook 'text-mode-hook #'flyspell-mode)) (esy/init-step bindings "Bind some keys." (keymap-global-set "C-c w" #'esy/eww) (keymap-global-set "C-c c" #'org-capture) (keymap-global-set "C-c l" #'org-store-link) (keymap-global-set "C-c a" #'org-agenda) (keymap-global-set "C-c v" #'vterm-other-window) (keymap-global-set "C-c p" sweeprolog-prefix-map) (keymap-global-set "C-c S" #'scratch-buffer) (keymap-global-set "C-c m" #'esy/emms-map) (keymap-global-set "C-c !" #'consult-flymake) (keymap-global-set "C-c C" #'esy-publish-create-post) (keymap-global-set "C-c O" #'openai-chat) (keymap-global-set "C-c E" #'elfeed) (keymap-global-set "C-c G" #'gnus) (keymap-global-set "C-c M" #'mastodon) (keymap-global-set "C-c F" #'esy/find-init-file) (keymap-global-set "C-c P" #'list-packages) (keymap-global-set "C-c SPC" #'consult-mark) (keymap-global-set "C-c 1" #'delete-other-windows) (keymap-global-set "<remap> <kill-region>" #'esy/kill-dwim) (keymap-global-set "<remap> <goto-line>" #'consult-goto-line) (keymap-global-set "<remap> <suspend-frame>" #'zap-up-to-char) (keymap-global-set "<remap> <imenu>" #'consult-imenu) (keymap-global-set "<remap> <make-frame>" #'duplicate-line) (keymap-global-set "M-#" #'dictionary-search) (keymap-global-set "M-o" #'previous-buffer) (keymap-global-set "M-O" #'next-buffer) (keymap-global-set "C-," #'backward-delete-char) (keymap-global-set "C-." #'embark-act) (keymap-global-set "C-;" #'avy-goto-char-timer) (keymap-global-set "C-s-f" #'toggle-frame-fullscreen) (keymap-global-set "C-s-l" #'esy/pulse-line) (keymap-set ctl-x-map "b" #'consult-buffer) (keymap-set ctl-x-4-map "b" #'consult-buffer-other-window) (keymap-set ctl-x-5-map "b" #'consult-buffer-other-frame) (keymap-set ctl-x-map "r b" #'consult-bookmark) (keymap-set ctl-x-map "C-b" #'ibuffer) (keymap-set search-map "r" #'rg) (keymap-set search-map "l" #'rg-literal) (keymap-set search-map "b" #'some-button) (keymap-set window-prefix-map "p" #'windmove-swap-states-up) (keymap-set window-prefix-map "n" #'windmove-swap-states-down) (keymap-set window-prefix-map "a" #'windmove-swap-states-left) (keymap-set window-prefix-map "e" #'windmove-swap-states-right) (dolist (command '(windmove-swap-states-up windmove-swap-states-down windmove-swap-states-left windmove-swap-states-right)) (put command 'repeat-map 'window-prefix-map)) ;; unbind some keys (dolist (key '("s-a" "s-d" "s-e" "s-f" "s-g" "s-h" "s-j" "s-k" "s-l" "s-m" "s-o" "s-p" "s-q" "s-s" "s-t" "s-u" "s-w" "s-x" "s-y" "s-z")) (keymap-global-unset key)) ;; enable some commands (dolist (command '(set-goal-column narrow-to-region narrow-to-page downcase-region upcase-region)) (put command 'disabled nil)) ;; disable some commands (put 'suspend-frame 'disabled t)) (esy/init-step project "Configure project management commands." (with-eval-after-load 'project (define-advice project--find-in-directory (:override (dir) no-remote-projects) (unless (file-remote-p dir) (run-hook-with-args-until-success 'project-find-functions dir))) (add-to-list 'project-switch-commands '(project-compile "Compile")) (add-to-list 'project-switch-commands '(rg-project "rg")) (add-to-list 'project-switch-commands '(magit-project-status "Magit")) (add-to-list 'project-switch-commands '(project-shell "Shell")) (when (boundp 'project-prefix-map) (define-key project-prefix-map "R" #'rg-project) (define-key project-prefix-map "m" #'magit-project-status)) (defvar esy/project-name-history nil) (defvar esy/projects-directory "~/checkouts/") (defun esy/read-project-by-name () "Read a project name and return its root directory. If no known project matches the selected name, prompt for a sub-directory of `esy/projects-directory' using the selected name as the initial input for completion, and return that directory." (let* ((name-dir-alist (delete nil (mapcar (lambda (dir) (when-let ((proj (project-current nil dir))) (cons (project-name proj) dir))) (project-known-project-roots)))) (current (project-current)) (default (and current (project-name current))) (name (completing-read (format-prompt "Project" default) name-dir-alist nil nil nil 'esy/project-name-history default))) (or (alist-get name name-dir-alist nil nil #'string=) (let* ((dir (read-directory-name "Project root directory: " esy/projects-directory nil t name)) (project (project-current nil dir))) (when project (project-remember-project project)) dir)))))) (esy/init-step sql "Configure SQL connections." (with-eval-after-load 'sql (setq sql-connection-alist (let* ((a (auth-source-search :port 5432 :max 7 :require '(:user :port :secret :host))) (d (nth 0 a)) (p (nth 1 a)) (c (nth 2 a)) (e (nth 3 a)) (f (nth 4 a)) (i (nth 5 a)) (g (nth 6 a))) `((dev (sql-product 'postgres) (sql-user ,(plist-get d :user)) (sql-port 5432) (sql-password ,(funcall (plist-get d :secret))) (sql-server ,(plist-get d :host)) (sql-database "alerts")) (prod (sql-product 'postgres) (sql-user ,(plist-get p :user)) (sql-port 5432) (sql-password ,(funcall (plist-get p :secret))) (sql-server ,(plist-get p :host)) (sql-database "alerts")) (cgs (sql-product 'postgres) (sql-user ,(plist-get c :user)) (sql-port 5432) (sql-password ,(funcall (plist-get c :secret))) (sql-server ,(plist-get c :host)) (sql-database "container_graph")) (ten (sql-product 'postgres) (sql-user ,(plist-get e :user)) (sql-port 5432) (sql-password ,(funcall (plist-get e :secret))) (sql-server ,(cadr (split-string (plist-get e :host) (rx "^")))) (sql-database ,(car (split-string (plist-get e :host) (rx "^"))))) (ac (sql-product 'postgres) (sql-user ,(plist-get f :user)) (sql-port 5432) (sql-password ,(funcall (plist-get f :secret))) (sql-server ,(cadr (split-string (plist-get f :host) (rx "^")))) (sql-database ,(car (split-string (plist-get f :host) (rx "^"))))) (int (sql-product 'postgres) (sql-user ,(plist-get i :user)) (sql-port 5432) (sql-password ,(funcall (plist-get i :secret))) (sql-server ,(cadr (split-string (plist-get i :host) (rx "^")))) (sql-database ,(car (split-string (plist-get i :host) (rx "^"))))) (fin (sql-product 'postgres) (sql-user ,(plist-get g :user)) (sql-port 5432) (sql-password ,(funcall (plist-get g :secret))) (sql-server ,(cadr (split-string (plist-get g :host) (rx "^")))) (sql-database ,(car (split-string (plist-get g :host) (rx "^")))))))) (add-hook 'sql-interactive-mode-hook #'toggle-truncate-lines) (define-key sql-mode-map (kbd "C-c C-f") #'sqlformat) (define-advice sql-comint-postgres (:around (fun &rest args) use-sql-password) (let ((process-environment (nconc (list (format "PGPASSWORD=%s" sql-password)) process-environment))) (apply fun args))))) (esy/init-step org "Configure Org mode." (with-eval-after-load 'org (keymap-unset org-mode-map "C-," t) (esy-publish-setup)) (with-eval-after-load 'org-agenda (add-to-list 'org-agenda-custom-commands '("w" "Work TODOs" tags-todo "+work")) (add-to-list 'org-agenda-custom-commands '("h" "Holland TODOs" tags-todo "+holland")))) (esy/init-step email "Configure email via `gnus'." (with-eval-after-load 'gnus (require 'gnus-icalendar) (add-hook 'gnus-group-mode-hook #'gnus-topic-mode) (gnus-icalendar-setup) (gnus-icalendar-org-setup) (all-the-icons-gnus-setup) (bbdb-initialize 'gnus 'mail 'message))) (esy/init-step dired "Configure `dired'." (defun esy/local-all-the-icons-dired-mode () (unless (file-remote-p default-directory) (all-the-icons-dired-mode))) (with-eval-after-load 'dired (put 'dired-find-alternate-file 'disabled nil) (add-hook 'dired-mode-hook #'esy/local-all-the-icons-dired-mode))) (esy/init-step tramp "Configure remote access via `tramp'." (with-eval-after-load 'tramp (tramp-set-completion-function "ssh" '((tramp-parse-netrc "~/.authinfo.gpg"))) (add-to-list 'backup-directory-alist (cons tramp-file-name-regexp nil)) (define-advice completion-file-name-table (:filter-args (args) no-remote-file-name-completion) (let ((string (car args)) (pred (cadr args)) (action (caddr args))) (if (and (file-remote-p string) (eq pred #'file-exists-p)) (list string nil action) (list string pred action)))))) (esy/init-step proced "Configure `proced'." (with-eval-after-load 'proced (add-hook 'proced-mode-hook (lambda () (setq proced-auto-update-flag t))))) (esy/init-step time "Configure `world-clock'." (with-eval-after-load 'time (add-to-list 'zoneinfo-style-world-list '("Europe/Amsterdam" "Amsterdam")))) (esy/init-step flyspell "Configure spelling errors correction via `flyspell'." (with-eval-after-load 'flyspell (keymap-unset flyspell-mode-map "C-," t) (keymap-unset flyspell-mode-map "C-." t) (keymap-unset flyspell-mode-map "C-;" t) (keymap-unset flyspell-mode-map "C-M-i" t))) (esy/init-step minibuffer-completion "Configure minibuffer completions." (defvar esy/completing-read-commands nil) (add-to-list 'savehist-additional-variables 'esy/completing-read-commands) (define-advice completing-read (:before (&rest _) record-command) (cl-incf (alist-get this-command esy/completing-read-commands 0))) (add-to-list 'display-buffer-alist '("\\*Completions\\*" (display-buffer-reuse-window display-buffer-at-bottom) (window-parameters (mode-line-format . none)))) (add-hook 'completion-list-mode-hook (lambda () (setq-local cursor-in-non-selected-windows nil))) (dolist (key-binding '(("C-p" . minibuffer-previous-completion) ("C-n" . minibuffer-next-completion) ("M-j" . minibuffer-force-complete-and-exit))) (keymap-set minibuffer-local-completion-map (car key-binding) (cdr key-binding))) (with-eval-after-load 'marginalia (add-hook 'marginalia-mode-hook #'all-the-icons-completion-marginalia-setup)) (with-eval-after-load 'consult (with-eval-after-load 'embark (require 'embark-consult)))) (esy/init-step capf "Set up some global `completion-at-point-functions'." (defun esy/dabbrev-capf () "Workaround for issue with `dabbrev-capf'." (require 'dabbrev) (dabbrev--reset-global-variables) (setq dabbrev-case-fold-search nil) (dabbrev-capf)) (add-to-list 'completion-at-point-functions #'esy/dabbrev-capf) (defun esy/file-capf () "File completion at point function." (let ((bs (bounds-of-thing-at-point 'filename))) (when bs (let* ((start (car bs)) (end (cdr bs))) `(,start ,end completion--file-name-table . (:exclusive no)))))) (add-to-list 'completion-at-point-functions #'esy/file-capf)) (esy/init-step lin "Configure highlighting of the current line via `lin'." (with-eval-after-load 'lin (add-to-list 'lin-mode-hooks 'gnus-summary-mode-hook) (add-to-list 'lin-mode-hooks 'gnus-group-mode-hook) (add-to-list 'lin-mode-hooks 'gnus-server-mode-hook))) (esy/init-step icons "Configure icons via `all-the-icons'." (with-eval-after-load 'all-the-icons (add-to-list 'all-the-icons-extension-icon-alist '("pl" all-the-icons-alltheicon "prolog" :height 1.1 :face all-the-icons-lmaroon)))) (esy/init-step corfu "Configure `completion-in-region' UI with `corfu'." (let ((original-completion-in-region-function completion-in-region-function)) (defun esy/setup-corfu (&rest args) (setq completion-in-region-function original-completion-in-region-function) (global-corfu-mode) (apply #'completion-in-region args))) (setq completion-in-region-function #'esy/setup-corfu) (with-eval-after-load 'corfu (defun esy/margin-formatter (metadata) "Format METADATA for `corfu-margin-formatters'." (pcase (cdr (assoc 'category metadata)) ('file (lambda (string) (concat (if (string-suffix-p "/" string) (all-the-icons-icon-for-dir string) (all-the-icons-icon-for-file string)) " "))) ('dabbrev (lambda (_) "… ")))) (add-to-list 'corfu-margin-formatters #'esy/margin-formatter) (corfu-indexed-mode))) (esy/init-step minor-modes "Enable some global minor modes." (dolist (mode '( column-number-mode context-menu-mode display-time-mode display-battery-mode global-auto-revert-mode global-diff-hl-mode global-whitespace-cleanup-mode lin-global-mode marginalia-mode minibuffer-depth-indicate-mode mouse-avoidance-mode pixel-scroll-precision-mode recentf-mode repeat-mode save-place-mode savehist-mode show-paren-mode transient-mark-mode )) (funcall mode))) (esy/init-step emms "Set up EMMS." (dolist (command '(emms-start emms-stop emms-next emms-previous emms-pause emms-seek emms-seek-to emms-seek-forward emms-seek-backward emms-show)) (autoload command "emms")) (defvar-keymap esy/emms-map :doc "My keymap for EMMS commands." :prefix 'esy/emms-map "N" #'emms-next "P" #'emms-previous "S" #'emms-stop "b" #'emms-seek-backward "f" #'emms-seek-forward "k" #'emms-seek "m" #'emms-show "p" #'emms-pause "s" #'emms-start "t" #'emms-seek-to) (with-eval-after-load 'emms (emms-minimalistic))) (esy/init-step pdf "Configure PDF handling." (with-eval-after-load 'pdf-view (require 'pdf-tools)) (with-eval-after-load 'pdf-tools (pdf-tools-install :no-query) (add-hook 'pdf-view-mode-hook #'pdf-view-midnight-minor-mode)) (add-to-list 'revert-without-query "\\.pdf\\'")) (esy/init-step tex "Configure TeX via `auctex'." (with-eval-after-load 'tex (add-hook 'TeX-after-compilation-finished-functions #'TeX-revert-document-buffer))) (esy/init-step vterm "Configure terminal emulation via `vterm'." (with-eval-after-load 'vterm (add-to-list 'vterm-tramp-shells '("kubernetes" "/bin/bash")))) (esy/init-step paredit "Remove some over-intrusive keybindings in `paredit'." (with-eval-after-load 'paredit (keymap-unset paredit-mode-map "M-s" t) (keymap-unset paredit-mode-map "M-?" t))) (esy/init-step sweep "Configure Prolog integration via `sweeprolog'." (add-to-list 'major-mode-remap-alist '(prolog-mode . sweeprolog-mode)) (with-eval-after-load 'sweeprolog (add-hook 'sweeprolog-mode-hook #'sweeprolog-electric-layout-mode) (add-hook 'sweeprolog-top-level-mode-hook #'compilation-shell-minor-mode))) (esy/init-step rg "Configure recursive grepping via `rg'." (with-eval-after-load 'rg (add-to-list 'rg-custom-type-aliases '("Prolog" . "*.pl *.plt *.pro *.prolog")))) (esy/init-step terraform "Configure custom faces in `terraform-mode'." (with-eval-after-load 'terraform-mode (setq terraform--resource-name-face font-lock-function-name-face terraform--resource-type-face font-lock-type-face))) (esy/init-step auto-mode "Associate major modes with files based on their extensions." (dolist (cell '(("Dockerfile" . dockerfile-ts-mode) ("\\.ya?ml\\'" . yaml-ts-mode) ("\\.toml\\'" . toml-ts-mode) ("\\.json\\'" . json-ts-mode) ("\\.tfstate\\'" . json-ts-mode) ("\\.ts\\'" . typescript-ts-mode) ("\\.rb\\'" . ruby-ts-mode) ("\\.rs\\'" . rust-ts-mode) ("\\.go\\'" . go-ts-mode) ("\\.pdf\\'" . pdf-view-mode) ("\\.plt?\\'" . prolog-mode))) (push cell auto-mode-alist))) (esy/init-step kill-hook "Compile this file (if changed) when Emacs is killed." (defun esy/compile-config () (interactive) (when (file-newer-than-file-p user-init-file (byte-compile-dest-file user-init-file)) (byte-compile-file user-init-file)) (when (file-newer-than-file-p early-init-file (byte-compile-dest-file early-init-file)) (byte-compile-file early-init-file))) (add-hook 'kill-emacs-hook #'esy/compile-config 10)) (esy/init-step help "Configure Help." (with-eval-after-load 'help-fns (with-eval-after-load 'shortdoc (add-hook 'help-fns-describe-function-functions #'shortdoc-help-fns-examples-function)))) (esy/init-step dict "Configure dictionary." (with-eval-after-load 'dictionary (setopt dictionary-search-interface 'help dictionary-default-dictionary "gcide" dictionary-default-strategy "prefix" dictionary-server "dict.org"))) (esy/init-step elfeed "Configure news feeds." (with-eval-after-load 'elfeed (setq ;; don't use bold face for unread Elfeed entries elfeed-search-face-alist '((unread (default))) ;; read feeds list from a separate file elfeed-feeds (with-temp-buffer (insert-file-contents-literally (locate-user-emacs-file "feeds.eld")) (goto-char (point-min)) (read (current-buffer)))))) (dolist (step (sort esy/init-step-times (lambda (l r) (time-less-p (cdr r) (cdr l))))) (message "%f %s" (float-time (cdr step)) (car step))) (provide 'init) ;;; init.el ends here