GNU Emacs Configuration
I’ve recently moved from a literate Emacs configuration based on Org
mode to a simpler init.el
file, reproduced below:
Main configuration
;;; 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: ;;; Temporarily increase GC threshold to expedite Emacs startup (let ((normal-gc-cons-threshold (* 20 1024 1024)) (init-gc-cons-threshold (* 1024 1024 1024))) (setq gc-cons-threshold init-gc-cons-threshold) (add-hook 'after-init-hook (lambda () (setq gc-cons-threshold normal-gc-cons-threshold)))) ;;; OS-specific settings (pcase system-type ('darwin (add-to-list 'exec-path "/usr/local/bin") (setq initial-frame-alist '((fullscreen . fullboth)) frame-title-format "Emacs") (set-fontset-font t '(? . ?) "SF Pro Display")) ('android (tool-bar-mode) (modifier-bar-mode) (visual-line-mode) (setq initial-frame-alist '((tool-bar-position . bottom))))) ;;; Check for external programs (unless (eq system-type 'android) (dolist (program '("autoconf" "automake" "aws" "bash" "cmake" "gcc" "git" "gpg" "go" "gopls" "gtar" "convert" "ispell" "jq" "mutool" "ninja" "psql" "rg" "stow" "makeinfo" "tree-sitter" "pandoc" "mpv")) (unless (executable-find program) (display-warning 'programming (format "Missing external program \"%s\"" program) :error)))) ;;; Set up Elpaca (defvar elpaca-installer-version 0.6) (defvar elpaca-directory (expand-file-name "elpaca/" user-emacs-directory)) (defvar elpaca-builds-directory (expand-file-name "builds/" elpaca-directory)) (defvar elpaca-repos-directory (expand-file-name "repos/" elpaca-directory)) (defvar elpaca-order '(elpaca :repo "https://github.com/progfolio/elpaca.git" :ref nil :files (:defaults (:exclude "extensions")) :build (:not elpaca--activate-package))) (let* ((repo (expand-file-name "elpaca/" elpaca-repos-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 (call-process "git" nil buffer t "clone" (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) (load "./elpaca-autoloads"))) (add-hook 'after-init-hook #'elpaca-process-queues) (elpaca `(,@elpaca-order)) ;;; Install packages (elpaca org-transclusion) (elpaca (sweeprolog :files ("*.org" "*.texi" "sweep.pl" "sweeprolog-pce-theme.el" "sweeprolog.el"))) (elpaca avy) (elpaca (bbdb :repo "https://git.savannah.nongnu.org/git/bbdb.git" :files (:defaults "lisp/*.el.in") :pre-build (("./autogen.sh") ("./configure") ("make")))) (elpaca (breadcrumb :repo "https://github.com/joaotavora/breadcrumb")) (elpaca (corfu :pre-build (("mv" "extensions/corfu-indexed.el" "corfu-indexed.el") ("rm" "-r" "extensions") ("emacs" "--batch" "-l" "ox-texinfo" "README.org" "--eval" "(setq org-babel-confirm-evaluate-answer-no t)" "-f" "org-texinfo-export-to-texinfo")))) (elpaca debbugs) (elpaca (devdocs :pre-build (("emacs" "--batch" "-l" "ox-texinfo" "README.org" "--eval" "(setq org-babel-confirm-evaluate-answer-no t)" "-f" "org-texinfo-export-to-texinfo") ("mv" "README.texi" "devdocs.texi")))) (elpaca diff-hl) (elpaca (eat :pre-build (("make" "terminfo") ("makeinfo" "--no-split" "eat.texi") ("touch" "dir") ("install-info" "--dir=dir" "eat.info")))) (elpaca (elfeed :pre-build (("pandoc" "-o" "elfeed.texi" "README.md")))) (elpaca embark-consult) (elpaca emms) (elpaca gnu-elpa-keyring-update) (elpaca htmlize) (elpaca keycast) (elpaca kubernetes) (elpaca (oauth2 :repo "git://git.sv.gnu.org/emacs/elpa" :local-repo "oauth2" :branch "externals/oauth2")) (elpaca (lin :pre-build (("emacs" "--batch" "-l" "ox-texinfo" "README.org" "--eval" "(setq org-babel-confirm-evaluate-answer-no t)" "-f" "org-texinfo-export-to-texinfo")))) (elpaca magit) (elpaca (markdown-mode :pre-build (("pandoc" "-o" "markdown-mode.texi" "README.md")))) (elpaca mastodon) (elpaca orderless) (elpaca (marginalia :pre-build (("emacs" "--batch" "-l" "ox-texinfo" "README.org" "--eval" "(setq org-babel-confirm-evaluate-answer-no t)" "-f" "org-texinfo-export-to-texinfo")) :repo "https://github.com/minad/marginalia.git")) (elpaca (osm :pre-build (("emacs" "--batch" "-l" "ox-texinfo" "README.org" "--eval" "(setq org-babel-confirm-evaluate-answer-no t)" "-f" "org-texinfo-export-to-texinfo")))) (elpaca (openai :repo "https://git.sr.ht/~eshel/openai.el")) (elpaca package-lint) (elpaca paredit) (elpaca rainbow-delimiters) (elpaca rainbow-mode) (elpaca rg) (elpaca sqlformat) (elpaca (mode-face :repo "https://git.sr.ht/~eshel/mode-face")) (elpaca terraform-mode) (elpaca whitespace-cleanup-mode) (elpaca rfc-mode) (elpaca (auctex :files ("*.el" "*.info*") :pre-build (("./autogen.sh") ("./configure" "--with-lispdir=." "--with-texmf-dir=/tmp") ("make") ("cp" "doc/auctex.info" "doc/auctex.info-1" "doc/auctex.info-2" "doc/preview-latex.info" ".")))) (elpaca (pdf-tools :pre-build (("emacs" "--batch" "-l" "ox-texinfo" "README.org" "--eval" "(setq org-babel-confirm-evaluate-answer-no t)" "--eval" "(setq org-export-with-broken-links t)" "-f" "org-texinfo-export-to-texinfo") ("mv" "README.texi" "pdf-tools.texi")))) (elpaca ob-prolog) (elpaca cape) (elpaca-wait) (defvar-keymap esy/elpaca-prefix-map :doc "Keymap for `elpaca' commands." "r" #'elpaca-rebuild "e" #'elpaca-recipe "f" #'elpaca-fetch "F" #'elpaca-fetch-all "u" #'elpaca-merge "U" #'elpaca-merge-all "v" #'elpaca-visit "d" #'elpaca-delete "l" #'elpaca-log "i" #'elpaca-info "t" #'elpaca-try) (defalias 'esy/elpaca-prefix-map esy/elpaca-prefix-map) ;;; Set some variables (setq ;; my name user-full-name "Eshel Yaron" ;; my email address user-mail-address "[email protected]" mail-user-agent 'gnus-user-agent read-mail-command 'gnus ;; 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 ;; 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 display-time-24hr-format t ;; enable recursive minibuffers enable-recursive-minibuffers t bug-reference-url-format "https://debbugs.gnu.org/%s" ;; save bookmarks immediately bookmark-save-flag 1 ;; show matches count in isearch prompt isearch-lazy-count t ;; kill Eat terminal buffers when their process exits eat-kill-buffer-on-exit t ;; don't spawn new frames in Ediff ediff-window-setup-function #'ediff-setup-windows-plain duplicate-line-final-position -1 tramp-kubernetes-namespace "argo" ;; 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) ("h" "Homework" entry (file+headline "~/org/inbox.org" "Tasks") "** TODO [#B] %^{Task} :studies: DEADLINE: %(format-time-string \"<%Y-%m-%d %H:%M>\" (org-read-date t t)) :PROPERTIES: :CreatedAt: %u :CapturedAt: %a :CapturedAs: Homework :END: %?" :prepend t :empty-lines 1)) ;; 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) ("C" . c)) ;; preview LaTeX fragments in Org buffers via SVG org-preview-latex-default-process 'dvisvgm ;; 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 osm-copyright nil ;; 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")) ;; use ISO format for calendar dates calendar-date-style 'iso ;; maintain legibility of rendered text in HTML mails shr-color-visible-luminance-min 75 ;; 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 MPV with EMMS emms-player-list '(emms-player-mpv) 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 'visible completions-max-height 16 completion-auto-wrap t completion-at-point-functions nil corfu-cycle t corfu-indexed-start 1 shell-kill-buffer-on-exit t compilation-scroll-output t display-time-default-load-average nil ;; allow disabling confirming before compilation via local variables safe-local-variable-values '((compilation-read-command . nil)) 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) eldoc-minor-mode-string nil flyspell-mode-line-string nil paredit-lighter nil eglot-confirm-server-edits 'diff ;; 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")))) TeX-view-program-selection '((output-pdf "PDF Tools")) TeX-source-correlate-start-server t TeX-data-directory "~/.emacs.d/elpaca/builds/auctex/" TeX-lisp-directory TeX-data-directory TeX-auto-save t TeX-parse-self t TeX-source-correlate-mode t TeX-electric-sub-and-superscript t ;; TeX-electric-escape t LaTeX-electric-left-right-brace t ;; Read remote man pages Man-support-remote-systems t) (setq-default indent-tabs-mode nil) ;;; Load my custom theme (add-to-list 'custom-theme-load-path (expand-file-name "theme/" user-emacs-directory)) (load-theme 'esy t) ;;; 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 'completions-auto-update-mode "completions-auto-update" nil t) (unless (eq system-type 'android) (add-to-list 'load-path "~/checkouts/esy-publish/") (autoload 'esy-publish-setup "esy-publish" nil t) (autoload 'esy-publish "esy-publish" nil t) (autoload 'esy-publish-local-server "esy-publish" nil t) (autoload 'esy-publish-create-post "esy-publish" nil t) (with-eval-after-load 'recentf (add-to-list 'recentf-exclude "~/checkouts/esy-publish/local/.*\.html"))) (require 'esy-comm) (add-to-list 'savehist-additional-variables 'esy-o365-token-refresh-last-time) (with-eval-after-load 'gnus-sum (keymap-unset gnus-summary-mode-map "M-#" t)) ;;; 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/emacs-patch (file) (interactive "fPatch file: ") (require 'emacsbug) (delete-other-windows) (let ((pbuf (get-buffer-create "*Patch*")) (subject nil)) (with-current-buffer pbuf (insert-file-contents file nil nil nil t) (setq subject (mail-fetch-field "Subject")) (diff-mode)) (compose-mail report-emacs-bug-address subject) (message-goto-body) (insert "Tags: patch\n\n\n\n") (mml-attach-file file "text/patch" nil "attachment") (message-goto-body) (forward-line 2) (display-buffer pbuf '(display-buffer-in-direction (direction . right))) (message-add-action (lambda () (when-let (window (get-buffer-window pbuf)) (quit-window nil window))) 'send 'kill))) (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)))) (defvar duplicate-line-final-position) (defun duplicate-line-stay (arg) (interactive "p") (let ((duplicate-line-final-position 0)) (duplicate-line arg))) (defun esy/ttyper () (interactive) (require 'eat) (let ((default-directory "~") (eat-kill-buffer-on-exit t)) (eat "ttyper -c ~/.config/ttyper/config.toml" t))) (defun esy/terminal (arg) (interactive "P") (if (file-remote-p default-directory) (let ((process-environment (cons "TERM=xterm-256color" process-environment))) (eat "/bin/bash" arg)) (eat nil arg))) (defun esy/hut-builds () (interactive) (require 'eat) (let ((eat-kill-buffer-on-exit nil)) (eat "hut builds show -f" t))) (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/tmp-dired () "Start Dired in ~/tmp." (interactive) (dired "~/tmp")) (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)))) (defface mode-face-lisp-interaction-mode '((t :background "#fff6d8")) "") (with-eval-after-load 'mode-face (add-to-list 'mode-face-modes 'lisp-interaction-mode)) (with-eval-after-load 'comint (keymap-set comint-mode-map "C-c C-q" #'esy/log-buffer) (add-hook 'comint-mode-hook #'completion-preview-mode)) (defun esy/recent-log-summary () "Display a summary of my website's most recent access log." (interactive) (async-shell-command (string-join (list "ssh" "[email protected]" (shell-quote-argument (string-join '("grep -E '\"GET.+\" 2' /var/log/apache2/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*"))) (defvar esy/clone-history nil) (defun esy/clone (remote depth) (interactive (list (let ((default (thing-at-point 'url))) (read-string (format-prompt "Clone" default) nil 'esy/clone-history default)) (and current-prefix-arg (prefix-numeric-value current-prefix-arg)))) (let ((dir (expand-file-name (file-name-base (car (url-path-and-query (url-generic-parse-url remote)))) "~/checkouts"))) (apply #'call-process (append (list "git" nil nil nil "clone") (when depth (list "--depth" (number-to-string depth))) (list remote dir))) (dired dir))) (defun esy/json-path-to-position (pos) "Return the JSON path from the document's root to the element at POS. The path is represented as a list of strings and integers, corresponding to the object keys and array indices that lead from the root to the element at POS." (named-let loop ((node (treesit-node-at pos)) (acc nil)) (if-let ((parent (treesit-parent-until node (lambda (n) (member (treesit-node-type n) '("pair" "array")))))) (loop parent (cons (pcase (treesit-node-type parent) ("pair" (treesit-node-text (treesit-node-child (treesit-node-child parent 0) 1) t)) ("array" (named-let check ((i 1)) (if (< pos (treesit-node-end (treesit-node-child parent i))) (/ (1- i) 2) (check (+ i 2)))))) acc)) acc))) (defun esy/json-path-at-point (point &optional kill) "Display the JSON path at POINT. When KILL is non-nil, kill it too. Interactively, POINT is point and KILL is the prefix argument." (interactive "d\nP") (let ((path (mapconcat (lambda (o) (format "%s" o)) (esy/json-path-to-position point) "."))) (if kill (progn (kill-new path) (message "Copied: %s" path)) (message path)) path)) (defun esy/transcribe () (interactive) (message "Recording...") (let ((process (start-process "ffmpeg" nil "ffmpeg" "-f" "avfoundation" "-i" "1:0" "-ar" "16000" "-y" "/tmp/foo.wav"))) (set-transient-map (make-sparse-keymap) nil (lambda () (message "Stopping recording") (interrupt-process process) (accept-process-output process 1 nil t) (message "Transcribing...") (message (string-trim-right (string-trim-left (with-temp-buffer (call-process "whisper" nil '(t nil) nil "-m" "/Users/eshelyaron/checkouts/whisper.cpp/models/ggml-base.en.bin" "--no-timestamps" "-f" "/tmp/foo.wav") (goto-char (point-min)) (while (search-forward "[BLANK_AUDIO]" nil t) (replace-match "" nil t)) (buffer-string)))))) "Recording... Press any key to stop"))) (defun esy/record (timeout) (interactive "p") (message "Recording...") (call-process "ffmpeg" nil nil nil "-f" "avfoundation" "-i" "1:0" "-t" (number-to-string (max timeout 2)) "-ar" "16000" "-y" "/tmp/foo.wav") (openai-chat (string-trim-right (string-trim-left (with-temp-buffer (call-process "whisper" nil '(t nil) nil "-m" "/Users/eshelyaron/checkouts/whisper.cpp/models/ggml-base.en.bin" "--no-timestamps" "-f" "/tmp/foo.wav") (goto-char (point-min)) (while (search-forward "[BLANK_AUDIO]" nil t) (replace-match "" nil t)) (buffer-string)))))) (defun esy/dedicate-window (window flag) (interactive (list (get-buffer-window) (not current-prefix-arg))) (message "Window is %s dedicated to buffer %s." (if flag (if (window-dedicated-p) "already" "now") "no longer") (buffer-name)) (set-window-dedicated-p window flag)) (defun esy/bump (tag-prefix) (interactive (list "v")) (require 'lisp-mnt) (require 'magit-apply) (let ((date (format-time-string "%F" (current-time))) (current-version (save-excursion (lm-header "package-version")))) (unless current-version (user-error "No Package-Version Elisp header found")) (let ((next-version (mapconcat #'number-to-string (pcase (version-to-list current-version) (`(,major ,minor ,patch) (list major (1+ minor) patch))) "."))) (with-current-buffer (find-file "NEWS.org") (goto-char (point-min)) (re-search-forward (concat "^\\(" org-outline-regexp "\\)") nil t) (beginning-of-line) (if (looking-at (rx "* Version " (group-n 1 (+ digit) "." (+ digit) "." (+ digit)) " in development")) (progn (setq next-version (match-string-no-properties 1)) (end-of-line) (delete-char -14) (insert "on " date) (forward-char 2)) (insert "* Version " next-version " on " date "\n\n"))) (lm-header "package-version") (delete-region (point) (pos-eol)) (insert next-version) (vc-print-root-log) (find-file-other-window "NEWS.org") (named-let loop () (recursive-edit) (unless (y-or-n-p "OK to stage, commit and tag the new version?") (loop))) (unless (= (call-process "git" nil nil nil "add" (buffer-file-name) "NEWS.org") 0) (error "Git add failed")) (unless (= (call-process "git" nil nil nil "commit" "-m" (concat "Announce recent changes " "in NEWS.org " "and bump version to " next-version)) 0) (error "Git commit failed")) (unless (= (call-process "git" nil nil nil "tag" "-s" "-m" (concat "Release version " next-version) (concat tag-prefix next-version)) 0) (error "Git tag failed"))))) ;;; 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))) ;;; 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) ;;; 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 ))) ;;; 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 completion-preview-mode )) (add-hook 'prog-mode-hook mode)) (add-hook 'lisp-data-mode-hook #'paredit-mode) (add-hook 'lisp-data-mode-hook (lambda () (when (require 'rainbow-delimiters nil t) (rainbow-delimiters-mode)))) ;;; Extend standard text mode hooks (add-hook 'text-mode-hook #'flyspell-mode) (add-hook 'text-mode-hook #'completion-preview-mode) ;;; Bind some keys (keymap-global-set "C-M-i" #'completion-at-point) (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" #'esy/terminal) (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" 'esy/elpaca-prefix-map) (keymap-global-set "C-c E" #'elfeed) (keymap-global-set "C-c T" #'esy/ttyper) (keymap-global-set "C-c R" #'esy/record) (keymap-global-set "C-c G" #'gnus) (keymap-global-set "C-c M" #'mastodon) (keymap-global-set "C-c t" #'esy/tmp-dired) (keymap-global-set "C-c F" #'esy/find-init-file) (keymap-global-set "C-c SPC" #'consult-mark) (keymap-global-set "C-c 1" #'delete-other-windows) (keymap-global-set "C-c 2" #'split-window-below) (keymap-global-set "C-c 3" #'split-window-right) (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 "s-n" #'duplicate-line) (keymap-global-set "s-p" #'duplicate-line-stay) (keymap-global-set "s-u" #'universal-argument) (keymap-global-set "s--" #'negative-argument) (keymap-global-set "M-#" #'dictionary-search) (keymap-global-set "M-o" #'previous-buffer) (keymap-global-set "M-O" #'next-buffer) (keymap-global-set "C-," #'delete-backward-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-global-set "M-\"" #'insert-pair) (keymap-global-set "M-[" #'insert-pair) (keymap-global-set "M-'" #'insert-pair) (keymap-global-set "M-]" #'up-list) (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) (keymap-set window-prefix-map "d" #'esy/dedicate-window) (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)) ;;; digit arguments with the super modifier (dotimes (i 10) (keymap-global-set (concat "s-" (number-to-string i)) #'digit-argument)) ;;; 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-q" "s-s" "s-t" "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) ;;; 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 "w" #'project-copy-relative-file-name-as-kill) (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)))) (defun project-copy-relative-file-name-as-kill () (interactive) (if-let ((project (project-current)) (root (expand-file-name (project-root project))) (directory-abbrev-alist (cons (cons root "") directory-abbrev-alist)) (fn (abbreviate-file-name (or (buffer-file-name) (expand-file-name default-directory))))) (if (string-empty-p fn) (user-error "At project root!") (progn (kill-new fn) (message (concat (propertize "Copied file name " 'face 'shadow) (propertize fn 'face 'bold) (propertize " relative to " 'face 'shadow) (propertize (project-name project) 'face 'italic) (propertize " project root directory" 'face 'shadow))))) (user-error "Not a project buffer!")))) ;;; Configure SQL connections (with-eval-after-load 'sql (defun esy/update-sql-connection-alist () (interactive) (auth-source-forget-all-cached) (setq sql-connection-alist (delete nil (mapcar (lambda (source) (pcase (split-string (plist-get source :host) (rx "^")) (`(,con ,db ,host) (list (intern con) (list 'sql-product ''postgres) (list 'sql-user (plist-get source :user)) (list 'sql-port (string-to-number (plist-get source :port))) (list 'sql-password (funcall (plist-get source :secret))) (list 'sql-server host) (list 'sql-database db))))) (auth-source-search :port 5432 :max 10))))) (esy/update-sql-connection-alist) (add-hook 'sql-interactive-mode-hook #'toggle-truncate-lines) (add-hook 'sql-interactive-mode-hook #'abbrev-mode) (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)))) ;;; Configure Org mode (with-eval-after-load 'org (keymap-unset org-mode-map "C-," t) (unless (eq system-type 'android) (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"))) ;;; Configure `dired' (with-eval-after-load 'dired (put 'dired-find-alternate-file 'disabled nil)) ;;; 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))))) ;;; Configure `proced' (with-eval-after-load 'proced (add-hook 'proced-mode-hook (lambda () (setq proced-auto-update-flag t)))) ;;; Configure `world-clock' (with-eval-after-load 'time (add-to-list 'zoneinfo-style-world-list '("Europe/Amsterdam" "Amsterdam")) (add-to-list 'zoneinfo-style-world-list '("Asia/Tel_Aviv" "Tel Aviv"))) ;;; 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)) ;;; 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) ("SPC" . nil))) (keymap-set minibuffer-local-completion-map (car key-binding) (cdr key-binding))) (with-eval-after-load 'consult (with-eval-after-load 'embark (require 'embark-consult))) ;;; Define some custom `completion-at-point-functions' (defun file-capf () "File completion at point function." (pcase (bounds-of-thing-at-point 'filename) (`(,beg . ,end) (list beg end #'completion-file-name-table :annotation-function (lambda (_) " File") :exclusive 'no)))) (add-hook 'completion-at-point-functions #'file-capf) ;;; 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)) ;;; Configure `completion-in-region' UI with `corfu' (with-eval-after-load 'corfu (defun esy/margin-formatter (metadata) "Format METADATA for `corfu-margin-formatters'." (pcase (cdr (assoc 'category metadata)) ('dabbrev (lambda (_) "… ")))) (add-to-list 'corfu-margin-formatters #'esy/margin-formatter) (corfu-indexed-mode)) ;;; 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 pixel-scroll-precision-mode recentf-mode repeat-mode save-place-mode savehist-mode show-paren-mode transient-mark-mode winner-mode completions-auto-update-mode global-corfu-mode mode-face-global-mode )) (funcall mode)) ;;; 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 :repeat t "N" #'emms-next "P" #'emms-previous "S" #'emms-stop "b" #'emms-seek-backward "f" #'emms-seek-fofrward "k" #'emms-seek "m" #'emms-show "p" #'emms-pause "s" #'emms-start "t" #'emms-seek-to) (with-eval-after-load 'emms (emms-minimalistic)) ;;; Configure PDF handling (pdf-loader-install) (add-to-list 'revert-without-query "\\.pdf\\'") ;;; 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) (with-eval-after-load 'eldoc (eldoc-add-command 'paredit-RET))) ;;; Configure Prolog integration via `sweeprolog' (setq-default prolog-system 'swi) (add-to-list 'major-mode-remap-alist '(prolog-mode . sweeprolog-mode)) (with-eval-after-load 'sweeprolog (setq sweeprolog-top-level-persistent-history (locate-user-emacs-file ".sweep_history")) (add-hook 'sweeprolog-mode-hook #'sweeprolog-electric-layout-mode) (add-hook 'sweeprolog-top-level-mode-hook #'compilation-shell-minor-mode)) ;;; Configure recursive grepping via `rg' (with-eval-after-load 'rg (add-to-list 'rg-custom-type-aliases '("Prolog" . "*.pl *.plt *.pro *.prolog"))) ;;; 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)) ;;; 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) ("\\.plt?\\'" . prolog-mode) ("\\.tex\\'" . TeX-latex-mode))) (push cell auto-mode-alist)) ;;; 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))) ;;; Configure dictionary (with-eval-after-load 'dictionary (setopt dictionary-search-interface 'help dictionary-default-dictionary "gcide" dictionary-default-strategy "prefix" dictionary-server "dict.org")) ;;; Configure news feeds (with-eval-after-load 'elfeed (keymap-set elfeed-show-mode-map "S-SPC" #'scroll-down-command) (defvar esy/feeds-file (locate-user-emacs-file "feeds.eld")) (defun esy/feeds () (with-temp-buffer (insert-file-contents-literally esy/feeds-file) (goto-char (point-min)) (read (current-buffer)))) (defun esy/read-feed-keywords () (mapcar #'intern (completing-read-multiple "Keywords: " (let ((keywords nil)) (dolist (feed-keywords (mapcar #'cdr (esy/feeds))) (mapc (lambda (keyword) (add-to-list 'keywords keyword)) feed-keywords)) (mapcar #'symbol-name keywords))))) (defun esy/add-feed (url keywords) (interactive (let ((default (thing-at-point-url-at-point))) (list (read-string (format-prompt "Feed URL" default) nil nil default) (esy/read-feed-keywords)))) (let ((feeds (cons (cons url keywords) (esy/feeds)))) (with-temp-buffer (pp (cons (cons url keywords) (esy/feeds)) (current-buffer)) (write-region (point-min) (point-max) esy/feeds-file)) (setq elfeed-feeds feeds))) (defun esy/update-feeds () (interactive) (setq elfeed-feeds (esy/feeds))) (with-eval-after-load 'eww (defun esy/eww-add-feed (url keywords) (interactive (list (eww-read-alternate-url) (esy/read-feed-keywords)) eww-mode) (esy/add-feed url keywords))) (setq ;; read feeds list from a separate file elfeed-feeds (esy/feeds))) ;;; Disable some minor-mode mode-line lighters (dolist (mm '((whitespace-cleanup-mode . whitespace-cleanup-mode) (outline . outline-minor-mode) (abbrev . abbrev-mode))) (with-eval-after-load (car mm) (setf (alist-get (cdr mm) minor-mode-alist) '("")))) ;;; Configure Texinfo mode (with-eval-after-load 'texinfo (add-hook 'texinfo-mode-hook #'abbrev-mode)) ;;; Configure TeX (with-eval-after-load 'tex (setopt TeX-modes '(tex-mode plain-tex-mode latex-mode doctex-mode)) (add-hook 'plain-TeX-mode-hook (lambda () (set (make-local-variable 'TeX-electric-math) (cons "$" "$")))) (add-hook 'TeX-after-compilation-finished-functions #'TeX-revert-document-buffer)) (with-eval-after-load 'latex (add-hook 'LaTeX-mode-hook (lambda () (set (make-local-variable 'TeX-electric-math) (cons "\\(" "\\)")))) (add-hook 'LaTeX-mode-hook #'LaTeX-math-mode) (defun LaTeX-mark-math () (interactive) (when-let ((beg (search-backward "\\(" nil t)) (end (search-forward "\\)" nil t))) (set-mark beg))) (keymap-set LaTeX-mode-map "C-c C-SPC" #'LaTeX-mark-math)) (with-eval-after-load 'elisp-mode (setq elisp-flymake-byte-compile-load-path (cons "./" load-path))) (dolist (mm '((go-ts-mode . go-ts-mode-hook) (typescript-ts-mode . typescript-ts-mode-hook) (python . python-base-mode-hook) (cc-mode . c-mode-hook))) (with-eval-after-load (car mm) (add-hook (cdr mm) #'eglot-ensure))) (with-eval-after-load 'completion-preview (push 'org-self-insert-command completion-preview-commands) (push 'paredit-backward-delete completion-preview-commands) (setq completion-preview-minimum-symbol-length 2) (keymap-set completion-preview-active-mode-map "M-n" #'completion-preview-next-candidate) (keymap-set completion-preview-active-mode-map "M-p" #'completion-preview-prev-candidate) (keymap-set completion-preview-active-mode-map "M-i" #'completion-preview-insert)) (load-file "/Users/eshelyaron/checkouts/agda/src/data/emacs-mode/agda2.el") (provide 'init) ;;; init.el ends here
Mail and other communication settings
;;; esy-comm.el --- My communication settings -*- lexical-binding: t; -*- ;; Copyright (C) 2023 Eshel Yaron ;; Author: Eshel Yaron <[email protected]> ;; Keywords: comm ;; This program is free software; you can redistribute it and/or modify ;; it under the terms of the GNU General Public License as published by ;; the Free Software Foundation, either version 3 of the License, or ;; (at your option) any later version. ;; This program is distributed in the hope that it will be useful, ;; but WITHOUT ANY WARRANTY; without even the implied warranty of ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ;; GNU General Public License for more details. ;; You should have received a copy of the GNU General Public License ;; along with this program. If not, see <https://www.gnu.org/licenses/>. ;;; Commentary: ;; ;;; Code: (require 'esy-o365) (defvar esy-comm-accounts '(("me" "[email protected]" "mail.eshelyaron.com" "mail.eshelyaron.com") ("gmail" "[email protected]" "imap.gmail.com" "smtp.gmail.com") ("swi" "[email protected]" "mail.swi-prolog.com" "mail.swi-prolog.com") ("uva" "[email protected]" "outlook.office365.com" "smtp.office365.com" (nnimap-authenticator xoauth2)))) (defvar esy-comm-summary-line-format (concat "%1{%U%R%z%}%B" "%2{%~(form (replace-regexp-in-string \" via .*\" \"\" (gnus-summary-from-or-to-or-newsgroups gnus-tmp-header gnus-tmp-from)))@%} " "%3{(%&user-date;, %k)%}:%* %s\n")) (setq ;; Message message-alternative-emails (regexp-opt (mapcar #'cadr esy-comm-accounts)) message-dont-reply-to-names message-alternative-emails message-elide-ellipsis "\n[...%l lines elided...]\n" message-server-alist (mapcar (pcase-lambda (`(,_ ,cond ,_ ,server . ,_)) (cons cond (format "smtp %s 587" server))) esy-comm-accounts) send-mail-function #'message-multi-smtp-send-mail ;; Gnus gnus-expert-user t gnus-always-read-dribble-file t gnus-break-pages nil gnus-inhibit-startup-message t gnus-cite-parse-max-size nil gnus-face-1 'bold gnus-face-2 'gnus-cite-1 gnus-face-3 'shadow gnus-summary-line-format esy-comm-summary-line-format gnus-simplify-subject-functions '(gnus-simplify-subject-re) gnus-treat-display-smileys nil gnus-select-method '(nntp "news.gmane.io") gnus-secondary-select-methods (cons '(nntp "news.eternal-september.org") (mapcar (pcase-lambda (`(,name ,_ ,address ,_ . ,tail)) `(nnimap ,name (nnimap-address ,address) (nnimap-server-port "imaps") (nnimap-stream ssl) . ,tail)) esy-comm-accounts)) gnus-no-groups-message "No new articles" gnus-use-full-window nil gnus-article-treat-types '("text/plain" "text/x-verbatim" "text/x-patch" "text/html" "text/calendar") gnus-icalendar-org-capture-file "~/org/inbox.org" gnus-icalendar-org-capture-headline '("Calendar") ;; Mastodon mastodon-instance-url "https://emacs.ch" mastodon-active-user "eshel" ;; BBDB bbdb-phone-style nil bbdb-complete-mail nil bbdb-complete-mail-allow-cycling t bbdb-completion-display-record nil ;; emacsbug.el report-emacs-bug-no-explanations t submit-emacs-patch-display-help nil ;; rcirc.el rcirc-default-nick "esy" rcirc-server-alist '(("irc.libera.chat" :channels ("#emacs") :port 6697 :encryption tls)) rcirc-log-flag t) (with-eval-after-load 'gnus (require 'gnus-icalendar) (add-hook 'gnus-group-mode-hook #'gnus-topic-mode) (unless (eq system-type 'android) (esy-o365-setup 'gnus)) (gnus-icalendar-setup) (gnus-icalendar-org-setup) (bbdb-initialize 'gnus 'mail 'message)) (with-eval-after-load 'message (add-hook 'message-send-hook 'ispell-message)) (provide 'esy-comm) ;;; esy-comm.el ends here
Custom libraries
Refresh OAuth 2.0 access token JIT for connecting to my university mail
;;; esy-o365.el --- Settings for outlook 365 -*- lexical-binding: t; -*- ;; Copyright (C) 2023 Eshel Yaron ;; Author: Eshel Yaron <[email protected]> ;; Keywords: comm ;; This program is free software; you can redistribute it and/or modify ;; it under the terms of the GNU General Public License as published by ;; the Free Software Foundation, either version 3 of the License, or ;; (at your option) any later version. ;; This program is distributed in the hope that it will be useful, ;; but WITHOUT ANY WARRANTY; without even the implied warranty of ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ;; GNU General Public License for more details. ;; You should have received a copy of the GNU General Public License ;; along with this program. If not, see <https://www.gnu.org/licenses/>. ;;; Commentary: ;; ;;; Code: (defvar esy-o365-token-directory "~/checkouts/M365-IMAP/") (defvar esy-o365-token-refresh-last-time 0) (defvar esy-o365-token-refresh-command '("python3" "refresh_token.py")) (defun esy-o365-refresh-token () (interactive) (files--ensure-directory esy-o365-token-directory) (when-let ((default-directory esy-o365-token-directory) (token (car (process-lines "python3" "refresh_token.py")))) (with-temp-buffer (insert "machine smtp.office365.com smtp-auth xoauth2 login [email protected] port 587 password \"" token "\"\nmachine outlook.office365.com login [email protected] port imaps password \"" token "\"\n") (write-region (point-min) (point-max) ".authinfo")) (auth-source-forget-all-cached))) (defun esy-o365-maybe-refresh-token () (let ((now (float-time))) (when (< (* 60 45) (- now esy-o365-token-refresh-last-time)) (esy-o365-refresh-token) (setq esy-o365-token-refresh-last-time now)))) ;;;###autoload (defun esy-o365-setup (mua) (require 'auth-source) (if (not (file-exists-p esy-o365-token-directory)) (let ((m "`esy-o365-token-directory' does not exist")) (if after-init-time (user-error m) (display-warning 'comm m))) (require 'auth-source) (add-to-list 'auth-sources (expand-file-name ".authinfo" esy-o365-token-directory)) (pcase mua ('gnus (dolist (hook '(gnus-before-startup-hook gnus-before-resume-hook gnus-select-group-hook gnus-get-new-news-hook)) (add-hook hook #'esy-o365-maybe-refresh-token)))))) (provide 'esy-o365) ;;; esy-o365.el ends here
Automatically update the completions buffer
;;; completions-auto-update.el --- Auto-update minibuffer completions -*- lexical-binding: t; -*- ;; Copyright (C) 2023 Eshel Yaron ;; Author: Eshel Yaron <[email protected]> ;; Keywords: convenience ;; This program is free software; you can redistribute it and/or modify ;; it under the terms of the GNU General Public License as published by ;; the Free Software Foundation, either version 3 of the License, or ;; (at your option) any later version. ;; This program is distributed in the hope that it will be useful, ;; but WITHOUT ANY WARRANTY; without even the implied warranty of ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ;; GNU General Public License for more details. ;; You should have received a copy of the GNU General Public License ;; along with this program. If not, see <https://www.gnu.org/licenses/>. ;;; Commentary: ;; This file defines a minor mode `completions-auto-update-mode' that ;; updates the *Completions* buffer as you type in the minibuffer. ;;; Code: (defcustom completions-auto-update-idle-time 0.2 "Number of seconds of idle to wait for before updating *Completions*." :group 'minibuffer :type 'number) (defvar-local completions-auto-update-timer nil) (defun completions-auto-update () "Update the *Completions* buffer, if it is visible." (when (get-buffer-window "*Completions*") (if completion-in-region-mode (completion-help-at-point) (minibuffer-completion-help))) (setq completions-auto-update-timer nil)) (defun completions-auto-update-start-timer () "Start an idle timer for updating *Completions*." (unless (or completions-auto-update-timer (not (get-buffer-window "*Completions*"))) (setq completions-auto-update-timer (run-with-idle-timer completions-auto-update-idle-time nil #'completions-auto-update)))) (defun completions-auto-update-setup () (add-hook 'post-self-insert-hook #'completions-auto-update-start-timer nil t)) (defun completions-auto-update-exit () (remove-hook 'post-self-insert-hook #'completions-auto-update-start-timer t)) (define-minor-mode completions-auto-update-mode "Update the *Completions* buffer as you type in the minibuffer." :global t :group 'minibuffer (if completions-auto-update-mode (progn (add-hook 'minibuffer-setup-hook #'completions-auto-update-setup) (add-hook 'minibuffer-setup-exit #'completions-auto-update-exit)) (remove-hook 'minibuffer-setup-hook #'completions-auto-update-setup) (remove-hook 'minibuffer-setup-exit #'completions-auto-update-exit))) (provide 'completions-auto-update) ;;; completions-auto-update.el ends here
My custom theme
;;; esy-theme.el --- My custom theme -*- lexical-binding:t -*- ;; Copyright (C) 2023 Eshel Yaron ;; This file is NOT part of GNU Emacs. ;;; Commentary: ;;; Code: (deftheme esy "My custom theme.") (custom-theme-set-faces 'esy `(default ((t . ,(append (when (eq system-type 'darwin) '(:height 130)) (unless (eq system-type 'android) '(:family "Iosevka")))))) `(fixed-pitch ((t . ,(unless (eq system-type 'android) '(:family "Iosevka"))))) `(variable-pitch ((t . ,(unless (eq system-type 'android) '(:family "Iosevka Etoile"))))) '(mode-line ((t))) '(mode-line-active ((t :overline "black" :background "lavender"))) '(mode-line-inactive ((t :underline "black"))) '(fringe ((t))) '(line-number-current-line ((t :background "yellow"))) '(fill-column-indicator ((t :foreground "orange" :weight light))) '(vertical-border ((t :foreground "grey"))) '(minibuffer-prompt ((t :weight bold :slant italic :foreground "red"))) '(italic ((t :slant italic))) '(cursor ((t :background "salmon"))) '(font-lock-doc-face ((t :inherit font-lock-string-face :slant italic))) '(elisp-shorthand-font-lock-face ((t :inherit font-lock-keyword-face :slant italic))) '(elfeed-search-title-face ((t :inherit shadow)))) (custom-theme-set-variables 'esy '(mode-line-format '(" %+ " (:eval (when-let (project (project-current)) (list (propertize (concat (project-name project) "/ ") 'face 'italic)))) (:propertize "%b" face mode-line-buffer-id) " (%[" mode-name mode-line-process minor-mode-alist (:eval (when (window-dedicated-p) " Dedicated")) (eglot--managed-mode (" " (:eval (when (eglot--mode-line-format))))) "%n%])" (vc-mode vc-mode) mode-line-format-right-align (:eval (when (mode-line-window-selected-p) (list "" 'display-time-string 'battery-mode-line-string " "))) "%5l" (4 "|%c") " " (-3 "%p") "/%I ")) '(elfeed-search-face-alist '((unread default))) '(mode-face-faces '(default fringe mode-line-inactive))) (provide-theme 'esy) ;;; esy-theme.el ends here