]> git.eshelyaron.com Git - dotfiles.git/commitdiff
update init.el
authorEshel Yaron <me@eshelyaron.com>
Thu, 16 Mar 2023 21:07:08 +0000 (23:07 +0200)
committerEshel Yaron <me@eshelyaron.com>
Sun, 19 Mar 2023 08:25:51 +0000 (10:25 +0200)
.emacs.d/init.el

index 960a665f021b2f6e7326de0b7a14ed2830617f2d..d55693fcda42698d2f2611455150d7f240ccf839 100644 (file)
@@ -1,21 +1,7 @@
 ;;; init.el --- Personal Emacs configuration -*- lexical-binding: t -*-
 ;; Copyright (C) 2021-2023 Eshel Yaron
 
-;; Author: Eshel Yaron <eshelshay.yaron@gmail.com>
-;; URL: https://eshelyaron.com/esy.html
-
-;; This file 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 file 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 file.  If not, see <http://www.gnu.org/licenses/>.
+;; Author: Eshel Yaron <me@eshelyaron.com>
 
 ;;; Commentary:
 
 
 ;;; Code:
 
+
+;;;; temporarly 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 'emacs-startup-hook
-            (lambda () (setq gc-cons-threshold normal-gc-cons-threshold))))
+      (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))))
 
-(require 'comp)
 (setq
+ ;; my name
+ user-full-name "Eshel Yaron"
+ ;; my email address
+ user-mail-address "me@eshelyaron.com"
+ ;; 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)
-
-(require 'package)
-(setq
+ warning-minimum-level :error
+ ;; don't use stale .elc files
+ load-prefer-newer t
+ ;; maximize Emacs on startup
+ initial-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
  ;; make some space in the Packages menu
  package-archive-column-width 12
  package-version-column-width 28
  ;; select some packages to install
- package-selected-packages
- '(
-   all-the-icons
-   all-the-icons-completion
-   all-the-icons-dired
-   all-the-icons-gnus
-   auctex
-   auctex-latexmk
-   avy
-   bbdb
-   corfu
-   define-word
-   denote
-   devdocs
-   diff-hl
-   ef-themes
-   elfeed
-   embark-consult
-   emms
-   gnu-elpa-keyring-update
-   gnuplot
-   graphviz-dot-mode
-   graphql-mode
-   haskell-mode
-   htmlize
-   ialign
-   jenkinsfile-mode
-   keycast
-   kubernetes
-   lin
-   magit
-   marginalia
-   markdown-mode
-   mastodon
-   no-littering
-   ob-prolog
-   orderless
-   org-modern
-   package-lint
-   paredit
-   pdf-tools
-   rainbow-delimiters
-   rainbow-mode
-   request
-   rg
-   slack
-   smtpmail-multi
-   sqlformat
-   typit
-   terraform-mode
-   vterm
-   vundo
-   whitespace-cleanup-mode
-   with-editor
-   ))
-
-(add-to-list 'package-archives
-             '("melpa" . "http://melpa.org/packages/"))
-(add-to-list 'package-archives
-             '("elpa-devel" . "https://elpa.gnu.org/devel/"))
-
-(package-install-selected-packages)
-
-(require 'no-littering)
-
-(setq auto-save-file-name-transforms
-      `((".*" ,(no-littering-expand-var-file-name "auto-save/") t)))
-
-(setq custom-file (no-littering-expand-etc-file-name "custom.el"))
-(load custom-file 'noerror 'nomessage)
-
-(when (fboundp 'startup-redirect-eln-cache)
-  (startup-redirect-eln-cache
-   (convert-standard-filename
-    (expand-file-name  "var/eln-cache/" user-emacs-directory))))
-
-(add-to-list 'load-path (expand-file-name  "lisp/" user-emacs-directory))
-
-(add-to-list 'initial-frame-alist '(fullscreen . fullboth))
-
-(set-face-attribute 'default nil
-                    :height 130
-                    :family "Iosevka")
-
-(set-face-attribute 'fixed-pitch nil
-                    :family "Iosevka")
-
-(set-face-attribute 'variable-pitch nil
-                    :family "Iosevka Etoile")
-
-(setq ef-themes-mixed-fonts       t
-      ef-themes-variable-pitch-ui t
-      ef-themes-to-toggle         '(ef-bio ef-day))
-(mapc #'disable-theme custom-enabled-themes)
-(load-theme 'ef-bio :no-confirm)
-
-(tool-bar-mode -1)
-(set-scroll-bar-mode nil)
-
-(setq use-dialog-box nil
-      inhibit-startup-screen t
-      initial-scratch-message ";; Go.\n"
-      ring-bell-function 'ignore
-      switch-to-buffer-obey-display-actions t)
-
-(setq-default indent-tabs-mode nil)
-
-(context-menu-mode)
-
-(pixel-scroll-precision-mode)
-
-(global-diff-hl-mode)
-
-(transient-mark-mode)
-
-(mouse-avoidance-mode 'banish)
-
-(show-paren-mode)
-
-(setq display-time-mail-function (lambda () nil))
-
-(display-time-mode)
-
-(column-number-mode)
-
-(display-battery-mode)
-
-(setq enable-recursive-minibuffers t)
-(minibuffer-depth-indicate-mode)
-
-(require '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)
-(lin-global-mode 1)
-
-(add-hook 'completion-list-mode-hook
-            (lambda ()
-              (setq-local cursor-in-non-selected-windows nil)))
-
-(recentf-mode 1)
-
-(save-place-mode 1)
-
-(setq bookmark-save-flag 1)
-
-(require 'savehist)
-(add-to-list 'savehist-additional-variables
-             'log-edit-comment-ring)
-(savehist-mode 1)
-
-(defconst esy/inbox-path (expand-file-name "inbox.org" "~/org")
-  "Path to my Org mode inbox file.")
-
-(defconst esy/journal-path (expand-file-name "journal.org" "~/org")
-  "Path to my Org mode journal file.")
-
-(with-eval-after-load 'org
-  (require 'ob)
-  (require 'ob-prolog)
-  (require 'ob-sql)
-  (require 'org-tempo)
-  (setq org-agenda-files `(,esy/inbox-path ,esy/journal-path)
-        org-default-notes-file esy/inbox-path
-        org-agenda-start-on-weekday 0
-        org-ellipsis "…"
-        org-use-speed-commands t
-        org-todo-keywords '((sequence
-                             "TODO(t)"
-                             "BLOCKED(b@/!)"
-                             "INPROGRESS(i!)"
-                             "|"
-                             "DONE(d!)"
-                             "CANCELED(c@)"))
-        org-tag-alist '((:startgroup)
-                        ("work"     . ?w)
-                        ("studies"  . ?s)
-                        ("esols"    . ?e)
-                        ("personal" . ?p)
-                        (:endgroup))
-        org-babel-load-languages '((emacs-lisp . t)
-                                   (shell      . t)
-                                   (sql        . t)
-                                   (bnf        . t)
-                                   (prolog     . t))
-        org-confirm-babel-evaluate nil
-        org-log-done 'time
-        org-log-into-drawer t
-        org-fast-tag-selection-single-key 'expert
-        org-use-fast-todo-selection 'expert
-        org-clock-in-switch-to-state "INPROGRESS")
-  (add-to-list 'org-src-lang-modes '("prolog" . sweeprolog))
-  (keymap-unset org-mode-map "C-," t))
-
-(with-eval-after-load 'org-agenda
-  (add-to-list 'org-agenda-custom-commands
-               '("w" "Work TODOs" tags-todo "+work")))
-
-(setq org-file-apps '((t . emacs)))
-
-(with-eval-after-load 'org-refile
-  (setq org-refile-targets '((org-agenda-files . (:maxlevel . 5))
-                             (nil . (:maxlevel . 3)))
-        org-archive-location "~/org/journal.org::datetree/* Finished Tasks                                              :ARCHIVE"
-        org-refile-use-outline-path t))
-
-(defun esy/org-fill-custom-id (point value)
-  "Set CUSTOM_ID to VALUE interactively for the entry at POINT."
-  (interactive "d\nMCUSTOM_ID: ")
-  (org-entry-put point "CUSTOM_ID" value))
-
-(defun esy/org-fill-description (point value)
-  "Set DESCRIPTION to VALUE interactively for the entry at POINT."
-  (interactive "d\nMDESCRIPTION: ")
-  (org-entry-put point "DESCRIPTION" value))
-
-(defun esy/org-maybe-prompt-custom-id ()
-  "Prompt for CUSTOM_ID if not set for the entry at POINT."
-  (let ((res 0))
-    (unless (and (org-entry-get (point) "DESCRIPTION")
-                 (org-entry-get (point) "CUSTOM_ID"))
-      (pulse-momentary-highlight-one-line)
-      (org-cycle)
-      (unless (org-entry-get (point) "CUSTOM_ID")
-        (call-interactively #'esy/org-fill-custom-id)
-        (setq res (1+ res)))
-      (unless (org-entry-get (point) "DESCRIPTION")
-        (call-interactively #'esy/org-fill-description)
-        (setq res (1+ res)))
-      (org-global-cycle 1))
-    res))
-
-(defun esy/org-fill-custom-ids-in-buffer ()
-  "Visit headers in the current buffer and set CUSTOM_ID for each."
-  (interactive)
-  (org-global-cycle 1)
-  (message "Filled %d properties."
-           (apply #'+ (remove nil
-                           (org-map-entries
-                            #'esy/org-maybe-prompt-custom-id)))))
-
-(defun esy/org-capture-to-project-heading ()
-  "Prompt for a projects and capture a related task."
-  (let* ((projects
-          (org-map-entries `(lambda () (nth 4 (org-heading-components)))
-                           "+project+LEVEL=2" `(,esy/inbox-path)))
-         (choice (completing-read "Project: " projects nil t nil))
-         (m (org-find-olp (cons
-                           (org-capture-expand-file esy/inbox-path)
-                           (list "Projects" choice)))))
-    (set-buffer (marker-buffer m))
-    (org-capture-put-target-region-and-position)
-    (widen)
-    (goto-char m)
-    (set-marker m nil)))
-
-(defun esy/org-capture-to-current-project ()
-  "Prompt for a projects and capture a related task."
-  (let* ((projects
-          (org-map-entries (lambda () (nth 4 (org-heading-components)))
-                           (concat "+project+LEVEL=2+SCM=\"file:"
-                                   (project-root (with-current-buffer
-                                                     (org-capture-get :original-buffer)
-                                                   (project-current)))
-                                   "\"")
-                           (list esy/inbox-path)))
-         (choice (car projects))
-         (m (org-find-olp (cons
-                           (org-capture-expand-file esy/inbox-path)
-                           (list "Projects" choice)))))
-    (set-buffer (marker-buffer m))
-    (org-capture-put-target-region-and-position)
-    (widen)
-    (goto-char m)
-    (set-marker m nil)))
-
-(setq org-capture-templates '(("t" "Todo [inbox]" entry
-                               (file+headline esy/inbox-path "Tasks")
-                               "** TODO %^{Task}    %^g
+ package-selected-packages '(
+                             all-the-icons
+                             all-the-icons-completion
+                             all-the-icons-dired
+                             all-the-icons-gnus
+                             auctex
+                             auctex-latexmk
+                             avy
+                             bbdb
+                             corfu
+                             define-word
+                             devdocs
+                             diff-hl
+                             elfeed
+                             embark-consult
+                             emms
+                             gnu-elpa-keyring-update
+                             gnuplot
+                             graphql-mode
+                             graphviz-dot-mode
+                             haskell-mode
+                             htmlize
+                             ialign
+                             keycast
+                             kubernetes
+                             lin
+                             magit
+                             marginalia
+                             markdown-mode
+                             mastodon
+                             ob-prolog
+                             orderless
+                             org-modern
+                             package-lint
+                             paredit
+                             pdf-tools
+                             rainbow-delimiters
+                             rainbow-mode
+                             request
+                             rg
+                             smtpmail-multi
+                             sqlformat
+                             sweeprolog
+                             terraform-mode
+                             typit
+                             vterm
+                             vundo
+                             whitespace-cleanup-mode
+                             with-editor
+                             )
+ ;; configure Org capture templates
+ org-capture-templates '(("t" "Todo [inbox]" entry
+                          (file+headline "~/org/inbox.org" "Tasks")
+                          "** TODO %^{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 esy/inbox-path "Tasks")
-                               "** TODO %^{Task}    :work:
+                          :prepend t
+                          :empty-lines 1
+                          :immediate-finish t)
+                         ("w" "Work [inbox]" entry
+                          (file+headline "~/org/inbox.org" "Tasks")
+                          "** TODO %^{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 esy/inbox-path "Calendar")
-                               "** %^{Title}    %^g
+                          :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
 :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)
-                              ("j" "Journal" entry
-                               (file+datetree esy/journal-path)
-                               "* %?
-:PROPERTIES:
-:CreatedAt: %u
-:CapturedAt: %a
-:CaptuerdAs: Journal entry
-:END:
-%i"
-                               :empty-lines 1)))
-
-(setq org-capture-templates-contexts
-      '(("P" (list project-current))))
-
-(setq user-full-name "Eshel Yaron")
-(setq user-mail-address "me@eshelyaron.com")
-
-(defconst esy/user-mail-address-gmail "eshelshay.yaron@gmail.com"
-  "My personal Gmail address.")
-
-(defconst esy/user-mail-address-swipl "eshel@swi-prolog.org"
-  "My SWI-Prolog email address.")
-
-(defconst esy/user-mail-address-me "me@eshelyaron.com"
-  "My persomal email address.")
-
-(defun esy/smtpmail-multi-make-accout (address server)
-  "Return an SMTP account definition for ADDRESS in SERVER."
-  `(,address ,server 587 ,address starttls nil nil nil))
-
-(defun esy/smtpmail-multi-make-rx (address)
-  "Return a regexp that matches ADDRESS wrapped with anything."
-  (rx (* anything) (literal address) (* anything)))
-
-(with-eval-after-load 'gnus
-  (require 'smtpmail-multi)
-  (require 'gnus-icalendar)
-  (require 'all-the-icons-gnus)
-
-  (setq smtpmail-multi-accounts
-        `((esy . ,(esy/smtpmail-multi-make-accout
-                   esy/user-mail-address-gmail
-                   "smtp.gmail.com"))
-          (swp . ,(esy/smtpmail-multi-make-accout
-                   esy/user-mail-address-swipl
-                   "mail.swi-prolog.com"))
-          (me  . ,(esy/smtpmail-multi-make-accout
-                   esy/user-mail-address-me
-                   "mail.eshelyaron.com")))
-        smtpmail-multi-associations
-        `((,(esy/smtpmail-multi-make-rx esy/user-mail-address-gmail) esy)
-          (,(esy/smtpmail-multi-make-rx esy/user-mail-address-swipl) swp)
-          (,(esy/smtpmail-multi-make-rx esy/user-mail-address-me) me))
-        send-mail-function #'smtpmail-multi-send-it
-        message-send-mail-function #'smtpmail-multi-send-it
-        mail-user-agent 'gnus-user-agent
-        gnus-always-read-dribble-file t
-        gnus-expert-user t
-        gnus-break-pages nil
-        gnus-inhibit-startup-message t
-        gnus-select-method '(nnimap "gmail"
-                                    (nnimap-address "imap.gmail.com")
-                                    (nnimap-server-port "imaps")
-                                    (nnimap-stream ssl))
-        gnus-secondary-servers '((nnimap "me"
+                          :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)
+                 ("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-rmail
+               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:"
+ ;; 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 . ("eshelshay.yaron@gmail.com"
+                                   "smtp.gmail.com" 587
+                                   "eshelshay.yaron@gmail.com"
+                                   starttls nil nil nil))
+                           (swp . ("eshel@swi-prolog.org"
+                                   "mail.swi-prolog.com" 587
+                                   "eshel@swi-prolog.org"
+                                   starttls nil nil nil))
+                           (me  . ("me@eshelyaron.com"
+                                   "mail.eshelyaron.com"
+                                   587
+                                   "me@eshelyaron.com"
+                                   starttls nil nil nil)))
+ ;; associate email addresses with SMTP accounts
+ smtpmail-multi-associations '(("[^z-a]*eshelshay\\.yaron@gmail\\.com[^z-a]*" esy)
+                               ("[^z-a]*eshel@swi-prolog\\.org[^z-a]*"        swp)
+                               ("[^z-a]*me@eshelyaron\\.com[^z-a]*"           me))
+ ;; 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
+ ;; 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-authinfo-file "~/.authinfo")))
-        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-posting-styles `((".*eshelyaron.com.*"
-                               (address ,esy/user-mail-address-me))
-                              (".*mail.swi-prolog.com.*"
-                               (address ,esy/user-mail-address-swipl))
-                              (".*"
-                               (address ,esy/user-mail-address-gmail)))
-        gnus-icalendar-org-capture-file esy/inbox-path
-        gnus-icalendar-org-capture-headline '("Calendar")
-        calendar-date-style 'iso)
+                                         (nnimap-stream ssl))
+                                 (nnimap "swi"
+                                         (nnimap-address "mail.swi-prolog.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)
+ bbdb-phone-style nil
+ eww-auto-rename-buffer 'title
+ browse-url-browser-function #'eww-browse-url
+ browse-url-generic-program "open"
+ ;; set up some feeds for Elfeed
+ elfeed-feeds '(
+                "https://ajroach42.com/feed.xml"
+                "https://alexschroeder.ch/wiki/feed/full/"
+                "https://amodernist.com/all.atom"
+                "https://archive.casouri.cc/note/atom.xml"
+                "https://arcology.garden/updates.xml"
+                "https://atthis.link/rss.xml"
+                "https://gwern.substack.com/feed"
+                "https://baty.net/feed"
+                "https://betterappsec.com/feed"
+                "https://bitspook.in/blog/feed.xml"
+                "https://blog.acthompson.net/feeds/posts/default"
+                "https://bruda.ca/feed.php"
+                "https://cce.whatthefuck.computer/updates.xml"
+                "https://cdn.jwz.org/blog/feed/"
+                "https://cestlaz.github.io/rss.xml"
+                "https://changelog.complete.org/feed"
+                "https://drewdevault.com/blog/index.xml"
+                "https://emacs.dyerdwelling.family/index.xml"
+                "https://erikmcclure.com/blog/index.xml"
+                "https://evanhahn.com/blog/index.xml"
+                "https://fasterthanli.me/index.xml"
+                "https://feeds.buzzsprout.com/2134279.rss"
+                "https://flower.codes/feed.xml"
+                "https://garymarcus.substack.com/feed"
+                "https://herman.bearblog.dev/feed/"
+                "https://jcm.libsyn.com/rss/"
+                "https://kitchen-sink.kwakk.info/feed"
+                "https://lars.ingebrigtsen.no/feed"
+                "https://lwn.net/headlines/rss"
+                "https://maggieappleton.com/rss.xml"
+                "https://matklad.github.io/feed.xml"
+                "https://matt-rickard.com/rss"
+                "https://njoseph.me/shaarli/feed/atom?"
+                "https://node2.feed43.com/7487052648530856.xml"
+                "https://nullprogram.com/feed/"
+                "https://olddeuteronomy.github.io/index.xml"
+                "https://parasurv.neocities.org/rss.xml"
+                "https://phaazon.net/blog/feed"
+                "https://planet.emacslife.com/atom.xml"
+                "https://ploum.net/atom_en.xml"
+                "https://pouria.dev/rss.xml"
+                "https://project-mage.org/rss.xml"
+                "https://reddit.com/r/prolog/.rss"
+                "https://sachachua.com/blog/feed/"
+                "https://stephanango.com/feed.xml"
+                "https://stppodcast.libsyn.com/rss"
+                "https://takeonrules.com/index.atom"
+                "https://two-wrongs.com/feed"
+                "https://usesthis.com/feed.atom"
+                "https://writer13.neocities.org/rss.xml"
+                "https://www.cs.uni.edu/~wallingf/blog/atom.xml"
+                "https://www.draketo.de/rss-feed.xml"
+                "https://www.evalapply.org/index.xml"
+                "https://www.fsf.org/static/fsforg/rss/news.xml"
+                "https://www.haskellforall.com/feeds/posts/default"
+                "https://www.murilopereira.com/index.xml"
+                "https://www.stilldrinking.org/rss/feed.xml"
+                "https://www.typetheoryforall.com/episodes.mp3.rss"
+                "https://xeiaso.net/blog.rss"
+                "https://xkcd.com/rss.xml"
+                )
+ global-auto-revert-non-file-buffers t
+ auto-revert-verbose nil
+ query-about-changed-file 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
+ completions-detailed nil
+ completion-styles '(orderless partial-completion basic)
+ completion-show-help nil
+ completions-header-format (propertize "%s candidates:\n" 'face 'shadow)
+ completion-auto-help 'visual
+ completions-max-height 16
+ completion-auto-wrap t
+ corfu-cycle t
+ corfu-margin-formatters '(esy/margin-formatter)
+ 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
+ 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
+ )
+
+;;;; load the modus-vivendi theme
+(require-theme 'modus-themes)
+(load-theme 'modus-vivendi)
+
+;;;; 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")
+
+(add-to-list 'load-path (expand-file-name "lisp/" user-emacs-directory))
 
-  (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))
+(setq-default indent-tabs-mode nil)
 
-(defun esy/find-init-file (init)
-  (interactive (list user-init-file))
-  (find-file init))
+(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)
   (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)
+  "Browse or search for TARGET."
+  (interactive (list (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)))))
+  (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))
+
+;;;; 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  )))
+
+;;;; set up external packages
+(require 'package)
+
+;;;; configure non-default package archive
+(add-to-list 'package-archives
+             '("melpa" . "http://melpa.org/packages/"))
+(add-to-list 'package-archives
+             '("elpa-devel" . "https://elpa.gnu.org/devel/"))
+
+;;;; install packages
+(package-install-selected-packages)
+
+;;;; enable some built-in tree-sitter modes
+(add-to-list 'auto-mode-alist '("Dockerfile"  . dockerfile-ts-mode))
+(add-to-list 'auto-mode-alist '("\\.ya?ml\\'" . yaml-ts-mode))
+(add-to-list 'auto-mode-alist '("\\.json\\'"  . json-ts-mode))
+(add-to-list 'auto-mode-alist '("\\.rs\\'"    . rust-ts-mode))
+(add-to-list 'auto-mode-alist '("\\.go\\'"    . go-ts-mode))
+
+;;;; enable spell checking in text buffers
+(add-hook 'text-mode-hook #'flyspell-mode)
+
+(add-hook 'prog-mode-hook #'rainbow-delimiters-mode)
+(add-hook 'prog-mode-hook #'display-line-numbers-mode)
+(add-hook 'prog-mode-hook #'display-fill-column-indicator-mode)
+(add-hook 'prog-mode-hook #'flyspell-prog-mode)
+(add-hook 'prog-mode-hook #'flymake-mode)
+
+;;;; 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 !" #'consult-flymake)
 (keymap-global-set "C-c C" #'openai-chat)
-(keymap-global-set "C-c D" #'denote)
 (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 V" #'vertalen-at-point)
 (keymap-global-set "C-c F" #'esy/find-init-file)
-(keymap-global-unset "s-a")
-(keymap-global-unset "s-d")
-(keymap-global-unset "s-e")
-(keymap-global-unset "s-f")
-(keymap-global-unset "s-g")
-(keymap-global-unset "s-h")
-(keymap-global-unset "s-j")
-(keymap-global-unset "s-k")
-(keymap-global-unset "s-l")
-(keymap-global-unset "s-m")
-(keymap-global-unset "s-o")
-(keymap-global-unset "s-q")
-(keymap-global-unset "s-s")
-(keymap-global-unset "s-t")
-(keymap-global-unset "s-u")
-(keymap-global-unset "s-w")
-(keymap-global-unset "s-x")
-(keymap-global-unset "s-y")
-(keymap-global-unset "s-z")
+(keymap-global-set "C-c SPC" #'consult-mark)
 (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-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)
 
+;;;; 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-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))
   (put command 'disabled nil))
 
+;;;; disable some commands
 (put 'suspend-frame 'disabled t)
 
-(with-eval-after-load 'magit
-  (setq magit-repository-directories '(("~/checkouts/" . 1))))
+(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)))
 
-(with-eval-after-load 'mastodon
-  (setq mastodon-instance-url "https://emacs.ch"
-        mastodon-active-user "eshel"))
+;;;; SQL servers
+(with-eval-after-load 'sql
+  (setq sql-connection-alist (let* ((a (auth-source-search :port 5432
+                                                           :max  5
+                                                           :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)))
+                               `((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 "^"))))))))
+  (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))))
 
-(with-eval-after-load 'tramp
-  (tramp-set-completion-function "ssh" '((tramp-parse-netrc "~/.authinfo.gpg"))))
+;;;; Org settings
+(with-eval-after-load 'org
+  (keymap-unset org-mode-map "C-," t))
 
-(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)))
+(with-eval-after-load 'org-agenda
+  (add-to-list 'org-agenda-custom-commands
+               '("w" "Work TODOs" tags-todo "+work")))
 
-(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))))
+;;;; mail settings
+(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))
 
 (defun esy/local-all-the-icons-dired-mode ()
   (unless (file-remote-p default-directory)
 
 (with-eval-after-load 'dired
   (put 'dired-find-alternate-file 'disabled nil)
-  (setq dired-dwim-target t)
   (add-hook 'dired-mode-hook #'esy/local-all-the-icons-dired-mode))
 
-(defun esy/vterm-mode-hook-function ()
-  (setq-local term-prompt-regexp "^[^#$%>\n]*[#$%>] *"))
-
-(with-eval-after-load 'vterm
-  (setq vterm-shell "/bin/zsh"
-        vterm-max-scrollback 2048
-        vterm-kill-buffer-on-exit nil
-        vterm-use-vterm-prompt-detection-method t)
-  (add-to-list 'vterm-tramp-shells '("kubernetes" "/bin/bash"))
-  (add-hook 'vterm-mode-hook #'esy/vterm-mode-hook-function))
-
-(with-eval-after-load 'elfeed
-  (setq elfeed-feeds
-        '(
-          "https://ajroach42.com/feed.xml"
-          "https://cdn.jwz.org/blog/feed/"
-          "https://jcm.libsyn.com/rss/"
-          "https://betterappsec.com/feed"
-          "https://emacs.dyerdwelling.family/index.xml"
-          "https://www.typetheoryforall.com/episodes.mp3.rss"
-          "https://www.fsf.org/static/fsforg/rss/news.xml"
-          "https://amodernist.com/all.atom"
-          "https://arcology.garden/updates.xml"
-          "https://takeonrules.com/index.atom"
-          "https://atthis.link/rss.xml"
-          "https://archive.casouri.cc/note/atom.xml"
-          "https://cestlaz.github.io/rss.xml"
-          "https://drewdevault.com/blog/index.xml"
-          "https://evanhahn.com/blog/index.xml"
-          "https://alexschroeder.ch/wiki/feed/full/"
-          "https://usesthis.com/feed.atom"
-          "https://xeiaso.net/blog.rss"
-          "https://changelog.complete.org/feed"
-          "https://herman.bearblog.dev/feed/"
-          "https://lwn.net/headlines/rss"
-          "https://maggieappleton.com/rss.xml"
-          "https://matt-rickard.com/rss"
-          "https://node2.feed43.com/7487052648530856.xml"
-          "https://njoseph.me/shaarli/feed/atom?"
-          "https://nullprogram.com/feed/"
-          "https://olddeuteronomy.github.io/index.xml"
-          "https://parasurv.neocities.org/rss.xml"
-          "https://phaazon.net/blog/feed"
-          "https://bruda.ca/feed.php"
-          "https://erikmcclure.com/blog/index.xml"
-          "https://blog.acthompson.net/feeds/posts/default"
-          "https://planet.emacslife.com/atom.xml"
-          "https://pouria.dev/rss.xml"
-          "https://project-mage.org/rss.xml"
-          "https://reddit.com/r/prolog/.rss"
-          "https://sachachua.com/blog/feed/"
-          "https://feeds.buzzsprout.com/2134279.rss"
-          "https://stephanango.com/feed.xml"
-          "https://stppodcast.libsyn.com/rss"
-          "https://writer13.neocities.org/rss.xml"
-          "https://www.draketo.de/rss-feed.xml"
-          "https://www.haskellforall.com/feeds/posts/default"
-          "https://cce.whatthefuck.computer/updates.xml"
-          "https://xkcd.com/rss.xml"
-          "https://bitspook.in/blog/feed.xml"
-          "https://flower.codes/feed.xml"
-          "https://www.cs.uni.edu/~wallingf/blog/atom.xml"
-          "https://matklad.github.io/feed.xml"
-          "https://www.evalapply.org/index.xml"
-          "https://www.stilldrinking.org/rss/feed.xml"
-          "https://fasterthanli.me/index.xml"
-          "https://kitchen-sink.kwakk.info/feed"
-          "https://lars.ingebrigtsen.no/feed"
-          )))
-
-(with-eval-after-load 'denote
-  (setq denote-directory "~/Notes"))
-
-(with-eval-after-load 'eww
-  (setq eww-auto-rename-buffer 'title)
-  (with-eval-after-load 'browse-url
-    (setq browse-url-browser-function #'eww-browse-url
-          browse-url-generic-program "open")))
-
-(defun esy/eww ()
-  "Prompt for a URL or keywords to search the web for."
-  (interactive)
-  (eww (mapconcat #'identity
-                  (completing-read-multiple "Browse or search: "
-                                            eww-prompt-history
-                                            nil nil nil
-                                            'eww-prompt-history
-                                            (car (eww-suggested-uris)))
-                  " ")))
+(with-eval-after-load 'tramp
+  (tramp-set-completion-function "ssh" '((tramp-parse-netrc "~/.authinfo.gpg"))))
 
-(with-eval-after-load 'browse-url
-  (setq browse-url-browser-function #'eww-browse-url))
+(with-eval-after-load 'proced
+  (add-hook 'proced-mode-hook #'proced-toggle-auto-update))
 
-(add-hook 'proced-mode-hook #'proced-toggle-auto-update)
+(with-eval-after-load 'time
+  (add-to-list 'zoneinfo-style-world-list '("Europe/Amsterdam" "Amsterdam")))
 
-(with-eval-after-load 'bbdb
-  (setq bbdb-phone-style nil))
+(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))
 
-(defun esy/slack-start ()
-  (interactive)
-  (require 'slack)
-  (require 'auth-source)
-  (slack-register-team :name "Dazz"
-                       :default t
-                       :token (auth-source-pick-first-password
-                               :host "dazz-io.slack.com"
-                               :user "eshel")
-                       :cookie (auth-source-pick-first-password
-                                :host "dazz-io.slack.com"
-                                :user "eshel^cookie"))
-  (setq slack-prefer-current-team t
-        slack-buffer-emojify t)
-  (slack-start))
+(add-to-list 'display-buffer-alist
+             '("\\*Completions\\*"
+               (display-buffer-reuse-window display-buffer-at-bottom)
+               (window-parameters . ((mode-line-format . none)))))
 
-(emms-minimalistic)
-(setq emms-player-list '(emms-player-mpv))
-
-(defun vertalen--on-success (&rest args)
-  "Process ARGS and display translation in a dedicated buffer."
-  (with-current-buffer-window "*vertalen*"
-      (with-selected-window (selected-window)
-        (unless (eq major-mode 'vertalen-mode)
-          `(nil . ((inhibit-same-window . t)))))
-      #'fit-window-to-buffer
-    (setq tabulated-list-entries (plist-get args :data))
-    (vertalen-mode)
-    (+ 2 (length tabulated-list-entries))))
-
-(defun vertalen--parse ()
-  (require 'dom)
-  "Parse buffer and return a list of translations."
-  (let* ((dom (libxml-parse-html-region (point-min) (point-max)))
-         (res (dom-by-class dom ".*result-item-translations.*"))
-         (ret nil))
-    (dolist (elem res)
-      (setq ret
-            `((nil ,(vector
-                     (apply #'concat
-                            (dom-strings
-                             (car (dom-by-class
-                                   elem
-                                   ".*result-item-source.*"))))
-                     (mapconcat #'identity
-                                (dom-strings
-                                 (car (dom-by-class
-                                       elem
-                                       ".*result-item-target.*")))
-                                ", ")))
-              . ,ret)))
-    (reverse ret)))
-
-(setq vertalen--source nil)
-(setq vertalen-history nil)
-
-(with-eval-after-load 'savehist
-  (add-to-list 'savehist-additional-variables
-               'vertalen-history))
-
-(defun vertalen (word)
-  "Translate WORD."
-  (interactive (list (read-string "Translate: " nil 'vertalen-history (thing-at-point 'word))))
-  (require 'request)
-  (setq vertalen--source word)
-  (request "https://www.vertalen.nu/vertaal"
-    :params `(("van" . "nl")
-              ("naar" . "en")
-              ("vertaal" . ,word))
-    :parser  #'vertalen--parse
-    :success #'vertalen--on-success))
-
-(defun vertalen-at-point ()
-  "Translate word at point."
-  (interactive nil vertalen-mode)
-  (vertalen (thing-at-point 'word t)))
-
-(defvar-keymap vertalen-mode-map
-  :doc "Keymap for `vertalen-mode' buffers."
-  "RET" #'vertalen-at-point)
-
-(define-derived-mode vertalen-mode tabulated-list-mode "Vertalen"
-  "Major mode for listing Dutch to English translations."
-  (setq tabulated-list-format [("Source Language" 64 t)
-                               ("Target Language" 32 t)])
-  (tabulated-list-init-header)
-  (tabulated-list-print)
-  (save-excursion
-    (goto-char (point-min))
-    (let ((inhibit-read-only t)
-          (word (search-forward vertalen--source nil t)))
-      (while word
-        (add-face-text-property (match-beginning 0) (match-end 0) 'success)
-        (setq word (search-forward vertalen--source nil t))))))
+(add-hook 'completion-list-mode-hook
+          (lambda ()
+            (setq-local cursor-in-non-selected-windows 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))))
+
+(define-key minibuffer-local-completion-map
+            [remap previous-line] #'minibuffer-previous-completion)
+(define-key minibuffer-local-completion-map
+            [remap next-line]     #'minibuffer-next-completion)
 
 (defun esy/dabbrev-capf ()
   "Workaround for issue with `dabbrev-capf'."
 
 (add-to-list 'completion-at-point-functions #'esy/file-capf)
 
+(require 'use-package)
+
+;;;; highlight the current line in line-oriented buffers
+(use-package lin
+  :config
+  (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))
+
+(use-package all-the-icons
+  :config
+  (add-to-list 'all-the-icons-extension-icon-alist
+               '("pl" all-the-icons-alltheicon "prolog"
+                 :height 1.1 :face all-the-icons-lmaroon)))
+
 (defun esy/margin-formatter (metadata)
   "Format METADATA for `corfu-margin-formatters'."
   (pcase (cdr (assoc 'category metadata))
                      " ")))
     ('dabbrev (lambda (_) "… "))))
 
-(setq corfu-cycle t
-      corfu-margin-formatters '(esy/margin-formatter)
-      corfu-indexed-start     1)
-(global-corfu-mode)
-(corfu-indexed-mode 1)
-
-(setq read-extended-command-predicate #'command-completion-default-include-p
-      completions-format 'one-column
-      completion-auto-select nil
-      completions-detailed nil
-      completion-styles '(orderless partial-completion basic)
-      completion-show-help nil
-      completions-header-format (propertize "%s candidates:\n"
-                                            'face 'shadow)
-      completion-auto-help 'visual
-      completions-max-height 16
-      completion-auto-wrap t)
-
-(define-key minibuffer-local-completion-map
-            [remap previous-line]
-            #'minibuffer-previous-completion)
-
-(define-key minibuffer-local-completion-map
-            [remap next-line]
-            #'minibuffer-next-completion)
-
-(add-hook 'marginalia-mode-hook
-          #'all-the-icons-completion-marginalia-setup)
-
-(marginalia-mode)
-
-(add-to-list 'display-buffer-alist
-             '("\\*Completions\\*"
-               (display-buffer-reuse-window display-buffer-at-bottom)
-               (window-parameters . ((mode-line-format . none)))))
-(add-to-list 'all-the-icons-extension-icon-alist
-             '("pl" all-the-icons-alltheicon "prolog"
-               :height 1.1 :face all-the-icons-lmaroon))
-
-(add-hook 'text-mode-hook #'flyspell-mode)
+(use-package marginalia
+  :config
+  (add-hook 'marginalia-mode-hook #'all-the-icons-completion-marginalia-setup))
+
+;; enable some global minor modes
+(dolist-with-progress-reporter (mode '(
+                                       column-number-mode
+                                       context-menu-mode
+                                       corfu-indexed-mode
+                                       display-time-mode
+                                       display-time-mode
+                                       global-auto-revert-mode
+                                       global-corfu-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
+                                       ))
+    "Enabling minor modes"
+    (funcall mode))
 
-(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))
+(emms-minimalistic)
 
+;;;; PDF settings
 (pdf-tools-install t)
 
 (add-hook 'pdf-view-mode-hook #'pdf-view-midnight-minor-mode)
 
-(setq TeX-view-program-selection '((output-pdf "PDF Tools"))
-      TeX-source-correlate-start-server t)
-
 (add-to-list 'revert-without-query "\\.pdf\\'")
 
-(add-hook 'TeX-after-compilation-finished-functions
-          #'TeX-revert-document-buffer)
-
 (add-to-list 'auto-mode-alist '("\\.pdf\\'" . pdf-view-mode))
 
-(setq safe-local-variable-values
-      '((compilation-read-command . nil)))
-
-(add-hook 'prog-mode-hook #'rainbow-delimiters-mode)
-(add-hook 'prog-mode-hook #'display-line-numbers-mode)
-(add-hook 'prog-mode-hook #'display-fill-column-indicator-mode)
-(add-hook 'prog-mode-hook #'flymake-mode)
+(with-eval-after-load 'tex
+  (add-hook 'TeX-after-compilation-finished-functions
+            #'TeX-revert-document-buffer))
 
-(add-hook 'lisp-data-mode-hook #'enable-paredit-mode)
+(use-package vterm
+  :config
+  (add-to-list 'vterm-tramp-shells '("kubernetes" "/bin/bash")))
 
-(add-hook 'haskell-mode-hook #'interactive-haskell-mode)
-(add-hook 'haskell-mode-hook #'haskell-decl-scan-mode)
-(add-hook 'haskell-mode-hook #'haskell-doc-mode)
+(use-package paredit
+  :hook lisp-data-mode
+  :config
+  (keymap-unset paredit-mode-map "M-s" t)
+  (keymap-unset paredit-mode-map "M-?" t))
 
-(require 'use-package)
+(use-package haskell-mode
+  :hook ((haskell-mode . interactive-haskell-mode)
+         (haskell-mode . haskell-decl-scan-mode)
+         (haskell-mode . haskell-doc-mode)))
 
 (use-package sweeprolog
   :mode ("\\.plt?\\'" . sweeprolog-mode)
   :bind-keymap ("C-c p" . sweeprolog-prefix-map)
   :config
   (add-hook 'sweeprolog-mode-hook #'sweeprolog-electric-layout-mode)
-  (add-hook 'sweeprolog-top-level-mode-hook
-            #'compilation-shell-minor-mode))
+  (add-hook 'sweeprolog-top-level-mode-hook #'compilation-shell-minor-mode)
+  (with-eval-after-load 'org
+    (add-to-list 'org-src-lang-modes '("prolog" . sweeprolog))))
 
-(with-eval-after-load 'rg
+(use-package rg
+  :config
   (add-to-list 'rg-custom-type-aliases '("Prolog" . "*.pl *.plt *.pro *.prolog")))
 
-(add-to-list 'auto-mode-alist '("Dockerfile" . dockerfile-ts-mode))
-
-(add-to-list 'auto-mode-alist '("\\.ya?ml\\'" . yaml-ts-mode))
-
-(add-to-list 'auto-mode-alist '("\\.json\\'" . json-ts-mode))
-
-(add-to-list 'auto-mode-alist '("\\.rs\\'" . rust-ts-mode))
-
-(add-to-list 'auto-mode-alist '("\\.go\\'" . go-ts-mode))
-
 (use-package terraform-mode
   :config
   (setq terraform--resource-name-face font-lock-function-name-face
         terraform--resource-type-face font-lock-type-face))
 
-(setq vc-follow-symlinks t)
-
-(with-eval-after-load 'project
-  (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)))
-
-(setq shell-kill-buffer-on-exit t)
-
-(defun shell-restart-process ()
-  "Restart process of `shell-mode' buffer."
-  (interactive)
-  (let* ((proc (get-buffer-process (current-buffer)))
-         (pid  (process-id proc))
-         (cmd  (caddr (process-command proc))))
-    (message "%s" cmd)
-    (delete-process proc)
-    (async-shell-command cmd)))
-
-(with-eval-after-load 'shell
-  (keymap-set shell-mode-map "C-c C-k" #'shell-restart-process)
-  (keymap-set shell-mode-map "SPC" #'comint-magic-space))
-
-(defvar esy/logs-directory "~/logs"
-  "Directory where some log files will be saved.")
-
-(defvar-local esy/log-buffer-filename nil
-  "File in which the current buffer is logged.")
-
-(defun esy/log-buffer ()
-  "Save the current buffer under `esy/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")
-                             esy/logs-directory)))))
-    (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))
-
-(setq global-auto-revert-non-file-buffers t
-      auto-revert-verbose nil
-      query-about-changed-file t
-      kill-do-not-save-duplicates t
-      show-trailing-whitespace t)
-(global-auto-revert-mode)
-(global-whitespace-cleanup-mode 1)
-
 (with-eval-after-load 'consult
   (with-eval-after-load 'embark
     (require 'embark-consult)))
 
-(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)
-
-(with-eval-after-load 'xref
-  (setq xref-show-definitions-function #'consult-xref
-        xref-show-xrefs-function       #'consult-xref
-        xref-search-program             'ripgrep))
-
-(with-eval-after-load 'time
-  (add-to-list 'zoneinfo-style-world-list '("Europe/Amsterdam" "Amsterdam")))
-
-(defun process-menu-restart-process ()
-  "Restart process at point in a `list-processes' buffer."
-  (interactive)
-  (let* ((pos (point))
-         (pid (tabulated-list-get-id))
-         (ent (tabulated-list-get-entry))
-         (cmd (combine-and-quote-strings
-               (seq-drop (split-string-shell-command (seq-elt ent 6))
-                         2))))
-    (delete-process pid)
-    (async-shell-command cmd)
-    (revert-buffer)))
-
-(with-eval-after-load 'simple
-  (keymap-set process-menu-mode-map
-              "r"
-              #'process-menu-restart-process))
-
-(repeat-mode)
-
-(setq find-sibling-rules '(("\\([^/]+\\)\\.c\\'" "\\1.h")))
-
-(defun esy/seconds-to-date-string (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))))
-
-(with-eval-after-load 'sql
-  (setq sqlformat-command 'pgformatter)
-  (define-key sql-mode-map (kbd "C-c C-f") 'sqlformat)
-  (setq sql-input-ring-file-name (expand-file-name ".sqli-history"
-                                                   no-littering-var-directory))
-  (setq sql-connection-alist
-        (let* ((a (auth-source-search :port 5432
-                                      :max  5
-                                      :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)))
-          `((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 "^"))))))))
+;;;; compile this file (if changed) when Emacs is killed
+(defun esy/compile-config ()
+  (when (file-newer-than-file-p user-init-file
+                                (byte-compile-dest-file user-init-file))
+    (byte-compile-file user-init-file)))
 
-  (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))))
+(add-hook 'kill-emacs-hook #'esy/compile-config 10)
 
 (provide 'init)
 ;;; init.el ends here