;;; Code:
-(esy/init-step 'init)
-
-;;;; temporarly increase GC threshold to expedite Emacs startup
-(let ((normal-gc-cons-threshold (* 20 1024 1024))
- (init-gc-cons-threshold (* 1024 1024 1024))
- (normal-mode-line-format mode-line-format))
- (setq gc-cons-threshold init-gc-cons-threshold
- mode-line-format nil)
- (add-hook 'after-init-hook
- (lambda () (setq gc-cons-threshold normal-gc-cons-threshold
- mode-line-format normal-mode-line-format))))
-(esy/init-step 'gc)
-
-(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
- ;; 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
- 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
- org-modern
- package-lint
- paredit
- pdf-tools
- rainbow-delimiters
- rainbow-mode
- request
- rg
- smtpmail-multi
- sqlformat
- sweeprolog
- terraform-mode
- 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 [#B] %^{Task} %^g
+(defvar esy/init-step-times nil)
+
+(defmacro esy/init-step (name _doc &rest body)
+ (declare (indent defun)
+ (doc-string 2))
+ (let ((start-time-symbol (gensym)))
+ `(let ((,start-time-symbol (current-time)))
+ ,@body
+ (push (cons ',name
+ (time-subtract (current-time) ,start-time-symbol))
+ esy/init-step-times))))
+
+(esy/init-step gc
+ "Temporarily increase GC threshold to expedite Emacs startup."
+ (let ((normal-gc-cons-threshold (* 20 1024 1024))
+ (init-gc-cons-threshold (* 1024 1024 1024))
+ (normal-mode-line-format mode-line-format))
+ (setq gc-cons-threshold init-gc-cons-threshold
+ mode-line-format nil)
+ (add-hook 'after-init-hook
+ (lambda () (setq gc-cons-threshold normal-gc-cons-threshold
+ mode-line-format normal-mode-line-format)))))
+
+(esy/init-step vars
+ "Set some variables."
+ (setq
+ ;; my name
+ user-full-name "Eshel Yaron"
+ ;; my email address
+ user-mail-address "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
+ ;; don't use stale .elc files
+ load-prefer-newer t
+ ;; maximize Emacs on startup
+ default-frame-alist '((fullscreen . fullboth))
+ ;; disable popup dialogs
+ use-dialog-box nil
+ ;; disable splash screen
+ inhibit-startup-screen t
+ ;; make the scratch message more concise
+ initial-scratch-message ";; Go.\n"
+ ;; don't ring the bell
+ ring-bell-function #'ignore
+ ;; make C-x b obey display-buffer-alist
+ switch-to-buffer-obey-display-actions t
+ ;; disable new mail mode line indication
+ display-time-mail-function #'ignore
+ ;; enable recursive minibuffers
+ enable-recursive-minibuffers t
+ ;; save bookmarks immediately
+ bookmark-save-flag 1
+ ;; make some space in the Packages menu
+ package-archive-column-width 12
+ package-version-column-width 28
+ ;; configure package archives
+ package-archives '(("melpa" . "http://melpa.org/packages/")
+ ("gnu" . "https://elpa.gnu.org/devel/")
+ ("nongnu" . "https://elpa.nongnu.org/nongnu-devel/"))
+ ;; select some packages to install
+ package-selected-packages '(
+ all-the-icons
+ all-the-icons-completion
+ all-the-icons-dired
+ all-the-icons-gnus
+ auctex
+ avy
+ bbdb
+ corfu
+ define-word
+ devdocs
+ diff-hl
+ elfeed
+ embark-consult
+ emms
+ gnu-elpa-keyring-update
+ gnuplot
+ graphql-mode
+ graphviz-dot-mode
+ htmlize
+ ialign
+ keycast
+ kubernetes
+ lin
+ magit
+ marginalia
+ markdown-mode
+ mastodon
+ ob-prolog
+ orderless
+ org
+ org-modern
+ package-lint
+ paredit
+ pdf-tools
+ rainbow-delimiters
+ rainbow-mode
+ rg
+ smtpmail-multi
+ sqlformat
+ sweeprolog
+ terraform-mode
+ vterm
+ vundo
+ whitespace-cleanup-mode
+ )
+ ;; configure Org capture templates
+ org-capture-templates '(("t" "Todo [inbox]" entry
+ (file+headline "~/org/inbox.org" "Tasks")
+ "** TODO [#B] %^{Task} %^g
:PROPERTIES:
:CreatedAt: %u
:CapturedAt: %a
:CapturedAs: Inbox Task
:END:"
- :prepend t
- :empty-lines 1
- :immediate-finish t)
- ("w" "Work [inbox]" entry
- (file+headline "~/org/inbox.org" "Tasks")
- "** TODO [#B] %^{Task} :work:
+ :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
+ :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))
- ;; 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 "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 '(
- ("http://node2.feed43.com/7487052648530856.xml" comics scifi)
- ("https://ajroach42.com/feed.xml")
- ("https://alexschroeder.ch/wiki/feed/full/" tech)
- ("https://amodernist.com/all.atom" emacs)
- ("https://archive.casouri.cc/note/atom.xml")
- ("https://arcology.garden/updates.xml")
- ("http://www.aaronsw.com/weblog/index.xml" blog tech)
- ("https://atthis.link/rss.xml" tech)
- ("https://daily.baty.net/feed/feed.xml" blog tech)
- ("https://baty.net/feed" tech)
- ("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/" tech)
- ("https://cestlaz.github.io/rss.xml" tech)
- ("https://changelog.complete.org/feed")
- ("https://daniel.haxx.se/blog/feed/" tech)
- ("https://drewdevault.com/blog/index.xml")
- ("https://emacs.dyerdwelling.family/index.xml")
- ("https://emacsninja.com/feed.atom" emacs)
- ("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://gwern.substack.com/feed" tech blog)
- ("https://herman.bearblog.dev/feed/")
- ("https://irreal.org/blog/?feed=rss2" emacs)
- ("https://jcm.libsyn.com/rss/")
- ("https://jnd.org/feed" tech philosophy)
- ("https://kelar.org/~bandali/atom.xml" tech blog)
- ("https://kitchen-sink.kwakk.info/feed" comics)
- ("https://lars.ingebrigtsen.no/feed" emacs)
- ("https://lwn.net/headlines/rss" linux)
- ("https://maggieappleton.com/rss.xml" design)
- ("https://matklad.github.io/feed.xml")
- ("https://matt-rickard.com/rss" tech)
- ("https://njoseph.me/shaarli/feed/atom?")
- ("https://nullprogram.com/feed/" tech)
- ("https://olddeuteronomy.github.io/index.xml")
- ("https://parasurv.neocities.org/rss.xml" tech blog)
- ("https://phaazon.net/blog/feed")
- ("https://planet.emacslife.com/atom.xml" emacs)
- ("https://ploum.net/atom_en.xml")
- ("https://pod.fossified.com/podcast.rss" tech podcast)
- ("https://pouria.dev/rss.xml")
- ("https://project-mage.org/rss.xml")
- ("https://protesilaos.com/master.xml" emacs philosophy)
- ("https://reddit.com/r/prolog/.rss" prolog)
- ("https://risky.biz/rss.xml" tech)
- ("https://sachachua.com/blog/feed/" emacs)
- ("https://stephanango.com/feed.xml")
- ("https://stppodcast.libsyn.com/rss")
- ("https://takeonrules.com/index.atom")
- ("https://two-wrongs.com/feed")
- ("https://typeclasses.substack.com/feed")
- ("https://unixsheikh.com/feed.rss" linux)
- ("https://usesthis.com/feed.atom")
- ("https://writer13.neocities.org/rss.xml")
- ("https://www.cs.uni.edu/~wallingf/blog/atom.xml")
- ("https://www.devever.net/~hl/index.feed" tech)
- ("https://www.draketo.de/rss-feed.xml")
- ("https://www.evalapply.org/index.xml" emacs)
- ("https://www.fsf.org/static/fsforg/rss/news.xml" tech)
- ("https://www.haskellforall.com/feeds/posts/default" tech)
- ("https://www.murilopereira.com/index.xml")
- ("https://www.stilldrinking.org/rss/feed.xml")
- ("https://www.theregister.com/headlines.atom" tech)
- ("https://www.typetheoryforall.com/episodes.mp3.rss" podcast)
- ("https://xeiaso.net/blog.rss" tech)
- ("https://xenodium.com/rss.xml" emacs)
- ("https://xkcd.com/rss.xml" comics tech)
- )
- ;; don't use bold face for unread Elfeed entries
- elfeed-search-face-alist '((unread (default)))
- 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
- ;; persist Git commit message history
- savehist-additional-variables '(log-edit-comment-ring)
- )
-(esy/init-step 'vars)
-
-;;;; load the modus-vivendi theme
-(require-theme 'modus-themes)
-(load-theme 'modus-vivendi)
-(esy/init-step 'theme)
-
-;;;; set up the Iosevka font family
-(set-face-attribute 'default nil :family "Iosevka" :height 130)
-(set-face-attribute 'fixed-pitch nil :family "Iosevka")
-(set-face-attribute 'variable-pitch nil :family "Iosevka Etoile")
-(esy/init-step 'fonts)
-
-(add-to-list 'load-path (expand-file-name "lisp/" user-emacs-directory))
-
-(setq-default indent-tabs-mode nil)
-
-(defvar-keymap transpose-lines-repeat-map
- :doc "Repeat map for \\[transpose-lines]"
- "C-t" #'transpose-lines)
-
-(put 'transpose-lines 'repeat-map 'transpose-lines-repeat-map)
-
-;;;; utility commands
-(defun esy/kill-dwim ()
- "When region is active, kill region, otherwise kill last word."
- (interactive)
- (if (region-active-p)
- (let ((beg (region-beginning))
- (end (region-end)))
- (kill-region beg end))
- (let ((end (point)))
- (backward-word)
- (kill-region (point) end))))
-
-(defun esy/pulse-line (&optional _)
- "Pulse current line."
- (interactive)
- (pulse-momentary-highlight-one-line))
-
-(defun esy/seconds-to-date-string (seconds)
- "Decode and display the date corresponding to SECONDS."
- (interactive (list (if (use-region-p)
- (string-to-number (buffer-substring (region-beginning)
- (region-end)))
- (read-number "Unix timestamp: " (round (float-time))))))
- (message (format-time-string "%FT%T%z" (seconds-to-time seconds))))
-
-(defun esy/find-init-file (init)
- "Find the Emacs INIT file."
- (interactive (list user-init-file))
- (find-file init))
-
-(defun esy/eww-target ()
- (require 'eww)
- (if (use-region-p)
- (let ((target (buffer-substring-no-properties (use-region-beginning)
- (use-region-end))))
- (add-to-history 'eww-prompt-history target)
- target)
- (completing-read "Browse or search: "
- eww-prompt-history nil nil nil
- 'eww-prompt-history
- (eww-suggested-uris))))
-
-(defun esy/eww (target)
- "Browse or search for TARGET."
- (interactive (list (esy/eww-target)))
- (eww target))
-
-(with-eval-after-load 'shell
- (keymap-set shell-mode-map "SPC" #'comint-magic-space))
-
-(defvar-local esy/log-buffer-filename nil
- "File in which the current buffer is logged.")
-
-(defun esy/log-buffer ()
- "Save the current buffer under the ~/logs/ directory."
- (interactive)
- (let ((filename (or esy/log-buffer-filename
- (setq esy/log-buffer-filename
- (expand-file-name
- (concat mode-name
- "_"
- (format-time-string
- "%Y%m%d%H%M%S"
- (current-time))
- ".log")
- "~/logs")))))
- (save-restriction
- (widen)
- (write-region (point-min)
- (point-max)
- filename))))
-
-(with-eval-after-load 'comint
- (keymap-set comint-mode-map "C-c C-q" #'esy/log-buffer))
-
-;;;; override the default startup message
-(define-advice startup-echo-area-message (:override () report-init-time)
- (format "%s started in %s. Hack away."
- (propertize "Emacs" 'face 'success)
- (propertize (emacs-init-time) 'face 'error )))
-
-(esy/init-step 'stuff)
-
-;;;; 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)
-
-(esy/init-step '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)
-(esy/init-step 'prog-mode-hooks)
-
-;;;; 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 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-set "C-c P" #'list-packages)
-(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-global-set "<remap> <imenu>" #'consult-imenu)
-(keymap-global-set "<remap> <make-frame>" #'duplicate-line)
-(keymap-global-set "M-#" #'define-word-at-point)
-(keymap-global-set "M-o" #'previous-buffer)
-(keymap-global-set "M-O" #'next-buffer)
-(keymap-global-set "C-," #'backward-delete-char)
-(keymap-global-set "C-." #'embark-act)
-(keymap-global-set "C-;" #'avy-goto-char-timer)
-(keymap-global-set "C-s-f" #'toggle-frame-fullscreen)
-(keymap-global-set "C-s-l" #'esy/pulse-line)
-
-(keymap-set ctl-x-map "b" #'consult-buffer)
-(keymap-set ctl-x-4-map "b" #'consult-buffer-other-window)
-(keymap-set ctl-x-5-map "b" #'consult-buffer-other-frame)
-(keymap-set ctl-x-map "r b" #'consult-bookmark)
-(keymap-set ctl-x-map "C-b" #'ibuffer)
-
-(keymap-set search-map "r" #'rg)
-(keymap-set search-map "l" #'rg-literal)
-(keymap-set search-map "b" #'some-button)
-(esy/init-step 'bindings)
-
-;;;; unbind some keys
-(dolist (key '("s-a" "s-d" "s-e" "s-f" "s-g" "s-h" "s-j" "s-k" "s-l"
- "s-m" "s-o" "s-p" "s-q" "s-s" "s-t" "s-u" "s-w" "s-x"
- "s-y" "s-z"))
- (keymap-global-unset key))
-
-;;;; enable some commands
-(dolist (command '(set-goal-column
- narrow-to-region
- narrow-to-page))
- (put command 'disabled nil))
-
-;;;; disable some commands
-(put 'suspend-frame 'disabled t)
-
-(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)))
-
-;;;; SQL servers
-(with-eval-after-load 'sql
- (setq sql-connection-alist (let* ((a (auth-source-search :port 5432
- :max 6
- :require '(:user :port :secret :host)))
- (d (nth 0 a))
- (p (nth 1 a))
- (c (nth 2 a))
- (e (nth 3 a))
- (f (nth 4 a))
- (i (nth 5 a)))
- `((dev
- (sql-product 'postgres)
- (sql-user ,(plist-get d :user))
- (sql-port 5432)
- (sql-password ,(funcall (plist-get d :secret)))
- (sql-server ,(plist-get d :host))
- (sql-database "alerts"))
- (prod
- (sql-product 'postgres)
- (sql-user ,(plist-get p :user))
- (sql-port 5432)
- (sql-password ,(funcall (plist-get p :secret)))
- (sql-server ,(plist-get p :host))
- (sql-database "alerts"))
- (cgs
- (sql-product 'postgres)
- (sql-user ,(plist-get c :user))
- (sql-port 5432)
- (sql-password ,(funcall (plist-get c :secret)))
- (sql-server ,(plist-get c :host))
- (sql-database "container_graph"))
- (ten
- (sql-product 'postgres)
- (sql-user ,(plist-get e :user))
- (sql-port 5432)
- (sql-password ,(funcall (plist-get e :secret)))
- (sql-server ,(cadr (split-string (plist-get e :host) (rx "^"))))
- (sql-database ,(car (split-string (plist-get e :host) (rx "^")))))
- (ac
- (sql-product 'postgres)
- (sql-user ,(plist-get f :user))
- (sql-port 5432)
- (sql-password ,(funcall (plist-get f :secret)))
- (sql-server ,(cadr (split-string (plist-get f :host) (rx "^"))))
- (sql-database ,(car (split-string (plist-get f :host) (rx "^")))))
- (int
- (sql-product 'postgres)
- (sql-user ,(plist-get i :user))
- (sql-port 5432)
- (sql-password ,(funcall (plist-get i :secret)))
- (sql-server ,(cadr (split-string (plist-get i :host) (rx "^"))))
- (sql-database ,(car (split-string (plist-get i :host) (rx "^"))))))))
- (add-hook 'sql-interactive-mode-hook #'toggle-truncate-lines)
- (define-key sql-mode-map (kbd "C-c C-f") #'sqlformat)
- (define-advice sql-comint-postgres (:around (fun &rest args) use-sql-password)
- (let ((process-environment
- (nconc
- (list (format "PGPASSWORD=%s" sql-password))
- process-environment)))
- (apply fun args))))
-
-;;;; Org settings
-(with-eval-after-load 'org
- (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")))
-
-;;;; 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)
- (all-the-icons-dired-mode)))
-
-(with-eval-after-load 'dired
- (put 'dired-find-alternate-file 'disabled nil)
- (add-hook 'dired-mode-hook #'esy/local-all-the-icons-dired-mode))
-
-;;;; workaround for Bug#62329
-;; (with-eval-after-load 'compile
-;; (require 'tramp-sh))
-
-(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)))
-
-(with-eval-after-load 'proced
- (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 'flyspell
- (keymap-unset flyspell-mode-map "C-," t)
- (keymap-unset flyspell-mode-map "C-." t)
- (keymap-unset flyspell-mode-map "C-;" t))
-
-(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)))
-
-(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'."
- (require 'dabbrev)
- (dabbrev--reset-global-variables)
- (setq dabbrev-case-fold-search nil)
- (dabbrev-capf))
-
-(add-to-list 'completion-at-point-functions #'esy/dabbrev-capf)
-
-(defun esy/file-capf ()
- "File completion at point function."
- (let ((bs (bounds-of-thing-at-point 'filename)))
- (when bs
- (let* ((start (car bs))
- (end (cdr bs)))
- `(,start ,end completion--file-name-table . (:exclusive no))))))
-
-(add-to-list 'completion-at-point-functions #'esy/file-capf)
-(esy/init-step 'capf)
-
-(require 'use-package)
-(esy/init-step '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))
-(esy/init-step 'lin)
-
-(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)))
-(esy/init-step 'icons)
-
-(defun esy/margin-formatter (metadata)
- "Format METADATA for `corfu-margin-formatters'."
- (pcase (cdr (assoc 'category metadata))
- ('file (lambda (string)
- (concat (if (string-suffix-p "/" string)
- (all-the-icons-icon-for-dir string)
- (all-the-icons-icon-for-file string))
- " ")))
- ('dabbrev (lambda (_) "… "))))
-
-(use-package marginalia
- :config
- (add-hook 'marginalia-mode-hook #'all-the-icons-completion-marginalia-setup))
-(esy/init-step 'marginalia)
-
-;; enable some global minor modes
-(dolist-with-progress-reporter (mode '(
- column-number-mode
- context-menu-mode
- corfu-indexed-mode
- display-time-mode
- display-battery-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))
-(esy/init-step 'minor-modes)
-
-(emms-minimalistic)
-(esy/init-step 'emms)
-
-;;;; PDF settings
-(use-package pdf-tools
- :mode ("\\.pdf\\'" . pdf-view-mode)
- :config
- (pdf-tools-install :no-query)
- (add-hook 'pdf-view-mode-hook #'pdf-view-midnight-minor-mode)
- (add-to-list 'revert-without-query "\\.pdf\\'"))
-(esy/init-step 'pdf)
-
-(with-eval-after-load 'tex
- (add-hook 'TeX-after-compilation-finished-functions
- #'TeX-revert-document-buffer))
-(esy/init-step 'tex)
-
-(with-eval-after-load 'vterm
- (add-to-list 'vterm-tramp-shells '("kubernetes" "/bin/bash")))
-;; (use-package vterm
-;; :config
-;; (add-to-list 'vterm-tramp-shells '("kubernetes" "/bin/bash")))
-(esy/init-step 'vterm)
-
-(use-package paredit
- :hook lisp-data-mode
- :config
- (keymap-unset paredit-mode-map "M-s" t)
- (keymap-unset paredit-mode-map "M-?" t))
-(esy/init-step 'paredit)
-
-(use-package haskell-mode
- :hook ((haskell-mode . interactive-haskell-mode)
- (haskell-mode . haskell-decl-scan-mode)
- (haskell-mode . haskell-doc-mode)))
-(esy/init-step 'haskell)
-
-(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)
+ :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 "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://snarfed.org/feed" tech)
+ ("https://www.nirandfar.com/rss" tech blog)
+ ("https://www.joelotter.com/posts/index.xml" indieweb)
+ ("https://dri.es/rss.xml" indieweb)
+ ("https://www.thisdaysportion.com/feed/index.xml" indieweb)
+ ("http://regex.info/blog/feed" tech blog japan)
+ ("https://eshelyaron.com/rss.xml" me)
+ ("https://www.brautaset.org/feed.xml" blog tech)
+ ("http://node2.feed43.com/7487052648530856.xml" comics scifi)
+ ("https://ajroach42.com/feed.xml")
+ ("https://writepermission.com/rss.xml" tech blog)
+ ("https://alexschroeder.ch/wiki/feed/full/" tech)
+ ("https://amodernist.com/all.atom" emacs)
+ ("https://archive.casouri.cc/note/atom.xml")
+ ("https://arcology.garden/updates.xml")
+ ("http://www.aaronsw.com/weblog/index.xml" blog tech)
+ ("https://atthis.link/rss.xml" tech)
+ ("https://daily.baty.net/feed/feed.xml" blog tech)
+ ("https://baty.net/feed" tech)
+ ("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/" tech)
+ ("https://cestlaz.github.io/rss.xml" tech)
+ ("https://changelog.complete.org/feed")
+ ("https://daniel.haxx.se/blog/feed/" tech)
+ ("https://drewdevault.com/blog/index.xml")
+ ("https://emacs.dyerdwelling.family/index.xml")
+ ("https://emacsninja.com/feed.atom" emacs)
+ ("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://gwern.substack.com/feed" tech blog)
+ ("https://herman.bearblog.dev/feed/")
+ ("https://irreal.org/blog/?feed=rss2" emacs)
+ ("https://jcm.libsyn.com/rss/")
+ ("https://jnd.org/feed" tech philosophy)
+ ("https://kelar.org/~bandali/atom.xml" tech blog)
+ ("https://kitchen-sink.kwakk.info/feed" comics)
+ ("https://lars.ingebrigtsen.no/feed" emacs)
+ ("https://lwn.net/headlines/rss" linux)
+ ("https://maggieappleton.com/rss.xml" design)
+ ("https://matklad.github.io/feed.xml")
+ ("https://matt-rickard.com/rss" tech)
+ ("https://njoseph.me/shaarli/feed/atom?")
+ ("https://nullprogram.com/feed/" tech)
+ ("https://olddeuteronomy.github.io/index.xml")
+ ("https://parasurv.neocities.org/rss.xml" tech blog)
+ ("https://phaazon.net/blog/feed")
+ ("https://planet.emacslife.com/atom.xml" emacs)
+ ("https://ploum.net/atom_en.xml")
+ ("https://pod.fossified.com/podcast.rss" tech podcast)
+ ("https://pouria.dev/rss.xml")
+ ("https://project-mage.org/rss.xml")
+ ("https://protesilaos.com/master.xml" emacs philosophy)
+ ("https://reddit.com/r/prolog/.rss" prolog)
+ ("https://risky.biz/rss.xml" tech)
+ ("https://sachachua.com/blog/feed/" emacs)
+ ("https://stephanango.com/feed.xml")
+ ("https://stppodcast.libsyn.com/rss")
+ ("https://takeonrules.com/index.atom")
+ ("https://two-wrongs.com/feed")
+ ("https://typeclasses.substack.com/feed")
+ ("https://unixsheikh.com/feed.rss" linux)
+ ("https://blog.gregbrockman.com/feed" tech ai)
+ ("https://usesthis.com/feed.atom")
+ ("https://writer13.neocities.org/rss.xml")
+ ("https://www.cs.uni.edu/~wallingf/blog/atom.xml")
+ ("https://www.devever.net/~hl/index.feed" tech)
+ ("https://www.draketo.de/rss-feed.xml")
+ ("https://www.evalapply.org/index.xml" emacs)
+ ("https://www.fsf.org/static/fsforg/rss/news.xml" tech)
+ ("https://www.haskellforall.com/feeds/posts/default" tech)
+ ("https://www.murilopereira.com/index.xml")
+ ("https://www.stilldrinking.org/rss/feed.xml")
+ ("https://www.theregister.com/headlines.atom" tech)
+ ("https://www.typetheoryforall.com/episodes.mp3.rss" podcast)
+ ("https://xeiaso.net/blog.rss" tech)
+ ("https://xenodium.com/rss.xml" emacs)
+ ("https://xkcd.com/rss.xml" comics tech)
+ ("http://sawv.org/rss.xml" tech blog)
+ )
+ ;; don't use bold face for unread Elfeed entries
+ elfeed-search-face-alist '((unread (default)))
+ 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-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
+ ;; persist Git commit message history
+ savehist-additional-variables '(log-edit-comment-ring)
+ )
+ (setq-default indent-tabs-mode nil))
+
+(esy/init-step theme
+ "Load the `modus-vivendi' theme."
+ (require-theme 'modus-themes)
+ (load-theme 'modus-vivendi))
+
+(esy/init-step fonts
+ "Set up the Iosevka font family."
+ (set-face-attribute 'default nil :family "Iosevka" :height 130)
+ (set-face-attribute 'fixed-pitch nil :family "Iosevka")
+ (set-face-attribute 'variable-pitch nil :family "Iosevka Etoile"))
+
+(esy/init-step load-path
+ "Add custom code directory to `load-path'."
+ (add-to-list 'load-path (expand-file-name "lisp/" user-emacs-directory))
+ (autoload 'some-button "some-button" nil t)
+ (autoload 'esy-capture "esy-capture" nil t)
+ (autoload 'pdf-view-mode "pdf-view" nil t))
+
+(esy/init-step commands
+ "Define custom commands."
+ (defvar-keymap transpose-lines-repeat-map
+ :doc "Repeat map for \\[transpose-lines]"
+ "C-t" #'transpose-lines)
+
+ (put 'transpose-lines 'repeat-map 'transpose-lines-repeat-map)
+
+ ;; utility commands
+ (defun esy/kill-dwim ()
+ "When region is active, kill region, otherwise kill last word."
+ (interactive)
+ (if (region-active-p)
+ (let ((beg (region-beginning))
+ (end (region-end)))
+ (kill-region beg end))
+ (let ((end (point)))
+ (backward-word)
+ (kill-region (point) end))))
+
+ (defun esy/pulse-line (&optional _)
+ "Pulse current line."
+ (interactive)
+ (pulse-momentary-highlight-one-line))
+
+ (defun esy/seconds-to-date-string (seconds)
+ "Decode and display the date corresponding to SECONDS."
+ (interactive (list (if (use-region-p)
+ (string-to-number (buffer-substring (region-beginning)
+ (region-end)))
+ (read-number "Unix timestamp: " (round (float-time))))))
+ (message (format-time-string "%FT%T%z" (seconds-to-time seconds))))
+
+ (defun esy/find-init-file (init)
+ "Find the Emacs INIT file."
+ (interactive (list user-init-file))
+ (find-file init))
+
+ (defun esy/eww-target ()
+ (require 'eww)
+ (if (use-region-p)
+ (let ((target (buffer-substring-no-properties (use-region-beginning)
+ (use-region-end))))
+ (add-to-history 'eww-prompt-history target)
+ target)
+ (completing-read "Browse or search: "
+ eww-prompt-history nil nil nil
+ 'eww-prompt-history
+ (eww-suggested-uris))))
+
+ (defun esy/eww (target)
+ "Browse or search for TARGET."
+ (interactive (list (esy/eww-target)))
+ (eww target))
+
+ (with-eval-after-load 'shell
+ (keymap-set shell-mode-map "SPC" #'comint-magic-space))
+
+ (defvar-local esy/log-buffer-filename nil
+ "File in which the current buffer is logged.")
+
+ (defun esy/log-buffer ()
+ "Save the current buffer under the ~/logs/ directory."
+ (interactive)
+ (let ((filename (or esy/log-buffer-filename
+ (setq esy/log-buffer-filename
+ (expand-file-name
+ (concat mode-name
+ "_"
+ (format-time-string
+ "%Y%m%d%H%M%S"
+ (current-time))
+ ".log")
+ "~/logs")))))
+ (save-restriction
+ (widen)
+ (write-region (point-min)
+ (point-max)
+ filename))))
+
+ (with-eval-after-load 'comint
+ (keymap-set comint-mode-map "C-c C-q" #'esy/log-buffer)))
+
+(esy/init-step startup-message
+ "Override the default startup message."
+ (define-advice startup-echo-area-message (:override () report-init-time)
+ (format "%s started in %s. Hack away."
+ (propertize "Emacs" 'face 'success)
+ (propertize (emacs-init-time) 'face 'error ))))
+
+(esy/init-step packages
+ "Ensure external packages are installed."
+ (package-install-selected-packages))
+
+(esy/init-step prog-mode-hook
+ "Extend standard programming mode hooks."
+ (dolist (mode '(bug-reference-prog-mode
+ display-fill-column-indicator-mode
+ display-line-numbers-mode
+ flymake-mode
+ flyspell-prog-mode
+ rainbow-delimiters-mode))
+ (add-hook 'prog-mode-hook mode))
+ (add-hook 'lisp-data-mode-hook #'paredit-mode))
+
+(esy/init-step other-hooks
+ "Extend other standard hooks."
+ (add-hook 'text-mode-hook #'flyspell-mode)
+ (add-hook 'completion-list-mode-hook
+ (lambda ()
+ (setq-local cursor-in-non-selected-windows nil))))
+
+(esy/init-step bindings
+ "Bind some keys."
+ (keymap-global-set "C-c w" #'esy/eww)
+ (keymap-global-set "C-c c" #'org-capture)
+ (keymap-global-set "C-c l" #'org-store-link)
+ (keymap-global-set "C-c a" #'org-agenda)
+ (keymap-global-set "C-c v" #'vterm-other-window)
+ (keymap-global-set "C-c 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-capture)
+ (keymap-global-set "C-c O" #'openai-chat)
+ (keymap-global-set "C-c E" #'elfeed)
+ (keymap-global-set "C-c G" #'gnus)
+ (keymap-global-set "C-c M" #'mastodon)
+ (keymap-global-set "C-c V" #'vertalen-at-point)
+ (keymap-global-set "C-c F" #'esy/find-init-file)
+ (keymap-global-set "C-c P" #'list-packages)
+ (keymap-global-set "C-c SPC" #'consult-mark)
+ (keymap-global-set "<remap> <kill-region>" #'esy/kill-dwim)
+ (keymap-global-set "<remap> <goto-line>" #'consult-goto-line)
+ (keymap-global-set "<remap> <suspend-frame>" #'zap-up-to-char)
+ (keymap-global-set "<remap> <imenu>" #'consult-imenu)
+ (keymap-global-set "<remap> <make-frame>" #'duplicate-line)
+ (keymap-global-set "M-#" #'define-word-at-point)
+ (keymap-global-set "M-o" #'previous-buffer)
+ (keymap-global-set "M-O" #'next-buffer)
+ (keymap-global-set "C-," #'backward-delete-char)
+ (keymap-global-set "C-." #'embark-act)
+ (keymap-global-set "C-;" #'avy-goto-char-timer)
+ (keymap-global-set "C-s-f" #'toggle-frame-fullscreen)
+ (keymap-global-set "C-s-l" #'esy/pulse-line)
+
+ (keymap-set ctl-x-map "b" #'consult-buffer)
+ (keymap-set ctl-x-4-map "b" #'consult-buffer-other-window)
+ (keymap-set ctl-x-5-map "b" #'consult-buffer-other-frame)
+ (keymap-set ctl-x-map "r b" #'consult-bookmark)
+ (keymap-set ctl-x-map "C-b" #'ibuffer)
+
+ (keymap-set search-map "r" #'rg)
+ (keymap-set search-map "l" #'rg-literal)
+ (keymap-set search-map "b" #'some-button)
+
+ ;; unbind some keys
+ (dolist (key '("s-a" "s-d" "s-e" "s-f" "s-g" "s-h" "s-j" "s-k" "s-l"
+ "s-m" "s-o" "s-p" "s-q" "s-s" "s-t" "s-u" "s-w" "s-x"
+ "s-y" "s-z"))
+ (keymap-global-unset key))
+
+ ;; enable some commands
+ (dolist (command '(set-goal-column
+ narrow-to-region
+ narrow-to-page
+ downcase-region
+ upcase-region))
+ (put command 'disabled nil))
+
+ ;; disable some commands
+ (put 'suspend-frame 'disabled t))
+
+(esy/init-step project
+ "Configure project management commands."
+ (with-eval-after-load 'project
+ (define-advice project--find-in-directory (:override (dir) no-remote-projects)
+ (unless (file-remote-p dir)
+ (run-hook-with-args-until-success 'project-find-functions dir)))
+ (add-to-list 'project-switch-commands '(project-compile "Compile"))
+ (add-to-list 'project-switch-commands '(rg-project "rg"))
+ (add-to-list 'project-switch-commands '(magit-project-status "Magit"))
+ (add-to-list 'project-switch-commands '(project-shell "Shell"))
+ (when (boundp 'project-prefix-map)
+ (define-key project-prefix-map "R" #'rg-project)
+ (define-key project-prefix-map "m" #'magit-project-status))))
+
+(esy/init-step sql
+ "Configure SQL connections."
+ (with-eval-after-load 'sql
+ (setq sql-connection-alist (let* ((a (auth-source-search :port 5432
+ :max 6
+ :require '(:user :port :secret :host)))
+ (d (nth 0 a))
+ (p (nth 1 a))
+ (c (nth 2 a))
+ (e (nth 3 a))
+ (f (nth 4 a))
+ (i (nth 5 a)))
+ `((dev
+ (sql-product 'postgres)
+ (sql-user ,(plist-get d :user))
+ (sql-port 5432)
+ (sql-password ,(funcall (plist-get d :secret)))
+ (sql-server ,(plist-get d :host))
+ (sql-database "alerts"))
+ (prod
+ (sql-product 'postgres)
+ (sql-user ,(plist-get p :user))
+ (sql-port 5432)
+ (sql-password ,(funcall (plist-get p :secret)))
+ (sql-server ,(plist-get p :host))
+ (sql-database "alerts"))
+ (cgs
+ (sql-product 'postgres)
+ (sql-user ,(plist-get c :user))
+ (sql-port 5432)
+ (sql-password ,(funcall (plist-get c :secret)))
+ (sql-server ,(plist-get c :host))
+ (sql-database "container_graph"))
+ (ten
+ (sql-product 'postgres)
+ (sql-user ,(plist-get e :user))
+ (sql-port 5432)
+ (sql-password ,(funcall (plist-get e :secret)))
+ (sql-server ,(cadr (split-string (plist-get e :host) (rx "^"))))
+ (sql-database ,(car (split-string (plist-get e :host) (rx "^")))))
+ (ac
+ (sql-product 'postgres)
+ (sql-user ,(plist-get f :user))
+ (sql-port 5432)
+ (sql-password ,(funcall (plist-get f :secret)))
+ (sql-server ,(cadr (split-string (plist-get f :host) (rx "^"))))
+ (sql-database ,(car (split-string (plist-get f :host) (rx "^")))))
+ (int
+ (sql-product 'postgres)
+ (sql-user ,(plist-get i :user))
+ (sql-port 5432)
+ (sql-password ,(funcall (plist-get i :secret)))
+ (sql-server ,(cadr (split-string (plist-get i :host) (rx "^"))))
+ (sql-database ,(car (split-string (plist-get i :host) (rx "^"))))))))
+ (add-hook 'sql-interactive-mode-hook #'toggle-truncate-lines)
+ (define-key sql-mode-map (kbd "C-c C-f") #'sqlformat)
+ (define-advice sql-comint-postgres (:around (fun &rest args) use-sql-password)
+ (let ((process-environment
+ (nconc
+ (list (format "PGPASSWORD=%s" sql-password))
+ process-environment)))
+ (apply fun args)))))
+
+(esy/init-step org
+ "Configure Org mode."
(with-eval-after-load 'org
- (add-to-list 'org-src-lang-modes '("prolog" . sweeprolog))))
-(esy/init-step 'sweep)
-
-(with-eval-after-load 'rg
- (add-to-list 'rg-custom-type-aliases '("Prolog" . "*.pl *.plt *.pro *.prolog")))
-;; (use-package rg
-;; :config
-;; (add-to-list 'rg-custom-type-aliases '("Prolog" . "*.pl *.plt *.pro *.prolog")))
-(esy/init-step 'rg)
-
-(use-package terraform-mode
- :config
- (setq terraform--resource-name-face font-lock-function-name-face
- terraform--resource-type-face font-lock-type-face))
-(esy/init-step 'terraform)
-
-(with-eval-after-load 'consult
- (with-eval-after-load 'embark
- (require 'embark-consult)))
-(esy/init-step 'embark-consult)
-
-;;;; 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)))
-
-(add-hook 'kill-emacs-hook #'esy/compile-config 10)
-(esy/init-step 'kill-hook)
-
-(let ((last nil))
- (dolist (step (reverse esy/init-step-times))
- (message "%s (%f) %s"
- (format-time-string "%T.%N" (cdr step))
- (if last
- (float-time (time-subtract (cdr step) last))
- 0)
- (car step))
- (setq last (cdr step))))
+ (keymap-unset org-mode-map "C-," t)
+ (add-to-list 'org-src-lang-modes '("prolog" . sweeprolog)))
+
+ (with-eval-after-load 'org-agenda
+ (add-to-list 'org-agenda-custom-commands
+ '("w" "Work TODOs" tags-todo "+work"))))
+
+(esy/init-step email
+ "Configure email via `gnus'."
+ (with-eval-after-load 'gnus
+ (require 'gnus-icalendar)
+ (add-hook 'gnus-group-mode-hook #'gnus-topic-mode)
+ (gnus-icalendar-setup)
+ (gnus-icalendar-org-setup)
+ (all-the-icons-gnus-setup)
+ (bbdb-initialize 'gnus 'mail 'message)))
+
+(esy/init-step dired
+ "Configure `dired'."
+ (defun esy/local-all-the-icons-dired-mode ()
+ (unless (file-remote-p default-directory)
+ (all-the-icons-dired-mode)))
+
+ (with-eval-after-load 'dired
+ (put 'dired-find-alternate-file 'disabled nil)
+ (add-hook 'dired-mode-hook #'esy/local-all-the-icons-dired-mode)))
+
+(esy/init-step tramp
+ "Configure remote access via `tramp'."
+ (with-eval-after-load 'tramp
+ (tramp-set-completion-function "ssh" '((tramp-parse-netrc "~/.authinfo.gpg")))
+ (add-to-list 'backup-directory-alist (cons tramp-file-name-regexp nil))))
+
+(esy/init-step proced
+ "Configure `proced'."
+ (with-eval-after-load 'proced
+ (add-hook 'proced-mode-hook #'proced-toggle-auto-update)))
+
+(esy/init-step time
+ "Configure `world-clock'."
+ (with-eval-after-load 'time
+ (add-to-list 'zoneinfo-style-world-list '("Europe/Amsterdam" "Amsterdam"))))
+
+(esy/init-step flyspell
+ "Configure spelling errors correction via `flyspell'."
+ (with-eval-after-load 'flyspell
+ (keymap-unset flyspell-mode-map "C-," t)
+ (keymap-unset flyspell-mode-map "C-." t)
+ (keymap-unset flyspell-mode-map "C-;" t)))
+
+(esy/init-step minibuffer-completion
+ "Configure minibuffer completions."
+ (add-to-list 'display-buffer-alist
+ '("\\*Completions\\*"
+ (display-buffer-reuse-window display-buffer-at-bottom)
+ (window-parameters . ((mode-line-format . none)))))
+
+ (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)
+
+ (with-eval-after-load 'marginalia
+ (add-hook 'marginalia-mode-hook #'all-the-icons-completion-marginalia-setup))
+
+ (with-eval-after-load 'consult
+ (with-eval-after-load 'embark
+ (require 'embark-consult))))
+
+(esy/init-step capf
+ "Set up some global `completion-at-point-functions'."
+ (defun esy/dabbrev-capf ()
+ "Workaround for issue with `dabbrev-capf'."
+ (require 'dabbrev)
+ (dabbrev--reset-global-variables)
+ (setq dabbrev-case-fold-search nil)
+ (dabbrev-capf))
+
+ (add-to-list 'completion-at-point-functions #'esy/dabbrev-capf)
+
+ (defun esy/file-capf ()
+ "File completion at point function."
+ (let ((bs (bounds-of-thing-at-point 'filename)))
+ (when bs
+ (let* ((start (car bs))
+ (end (cdr bs)))
+ `(,start ,end completion--file-name-table . (:exclusive no))))))
+
+ (add-to-list 'completion-at-point-functions #'esy/file-capf))
+
+(esy/init-step lin
+ "Configure highlighting of the current line via `lin'."
+ (with-eval-after-load 'lin
+ (add-to-list 'lin-mode-hooks 'gnus-summary-mode-hook)
+ (add-to-list 'lin-mode-hooks 'gnus-group-mode-hook)
+ (add-to-list 'lin-mode-hooks 'gnus-server-mode-hook)))
+
+(esy/init-step icons
+ "Configure icons via `all-the-icons'."
+ (with-eval-after-load 'all-the-icons
+ (add-to-list 'all-the-icons-extension-icon-alist
+ '("pl" all-the-icons-alltheicon "prolog"
+ :height 1.1 :face all-the-icons-lmaroon))))
+
+(esy/init-step corfu
+ "Configure `completion-in-region' UI with `corfu'."
+ (let ((original-completion-in-region-function completion-in-region-function))
+ (defun esy/setup-corfu (&rest args)
+ (setq completion-in-region-function original-completion-in-region-function)
+ (global-corfu-mode)
+ (apply #'completion-in-region args)))
+ (setq completion-in-region-function #'esy/setup-corfu)
+ (with-eval-after-load 'corfu
+ (defun esy/margin-formatter (metadata)
+ "Format METADATA for `corfu-margin-formatters'."
+ (pcase (cdr (assoc 'category metadata))
+ ('file (lambda (string)
+ (concat (if (string-suffix-p "/" string)
+ (all-the-icons-icon-for-dir string)
+ (all-the-icons-icon-for-file string))
+ " ")))
+ ('dabbrev (lambda (_) "… "))))
+ (add-to-list 'corfu-margin-formatters #'esy/margin-formatter)
+ (corfu-indexed-mode)))
+
+(esy/init-step minor-modes
+ "Enable some global minor modes."
+ (dolist (mode '(
+ column-number-mode
+ context-menu-mode
+ display-time-mode
+ display-battery-mode
+ global-auto-revert-mode
+ global-diff-hl-mode
+ global-whitespace-cleanup-mode
+ lin-global-mode
+ marginalia-mode
+ minibuffer-depth-indicate-mode
+ mouse-avoidance-mode
+ pixel-scroll-precision-mode
+ recentf-mode
+ repeat-mode
+ save-place-mode
+ savehist-mode
+ show-paren-mode
+ transient-mark-mode
+ ))
+ (funcall mode)))
+
+(esy/init-step emms
+ "Set up EMMS."
+ (dolist (command '(emms-start
+ emms-stop
+ emms-next
+ emms-previous
+ emms-pause
+ emms-seek
+ emms-seek-to
+ emms-seek-forward
+ emms-seek-backward
+ emms-show))
+ (autoload command "emms"))
+
+ (defvar-keymap esy/emms-map
+ :doc "My keymap for EMMS commands."
+ :prefix 'esy/emms-map
+ "N" #'emms-next
+ "P" #'emms-previous
+ "S" #'emms-stop
+ "b" #'emms-seek-backward
+ "f" #'emms-seek-forward
+ "k" #'emms-seek
+ "m" #'emms-show
+ "p" #'emms-pause
+ "s" #'emms-start
+ "t" #'emms-seek-to)
+
+ (with-eval-after-load 'emms
+ (emms-minimalistic)))
+
+(esy/init-step pdf
+ "Configure PDF handling."
+ (with-eval-after-load 'pdf-view
+ (require 'pdf-tools))
+ (with-eval-after-load 'pdf-tools
+ (pdf-tools-install :no-query)
+ (add-hook 'pdf-view-mode-hook #'pdf-view-midnight-minor-mode))
+ (add-to-list 'revert-without-query "\\.pdf\\'"))
+
+(esy/init-step tex
+ "Configure TeX via `auctex'."
+ (with-eval-after-load 'tex
+ (add-hook 'TeX-after-compilation-finished-functions
+ #'TeX-revert-document-buffer)))
+
+(esy/init-step vterm
+ "Configure terminal emulation via `vterm'."
+ (with-eval-after-load 'vterm
+ (add-to-list 'vterm-tramp-shells '("kubernetes" "/bin/bash"))))
+
+(esy/init-step paredit
+ "Remove some over-intrusive keybindings in `paredit'."
+ (with-eval-after-load 'paredit
+ (keymap-unset paredit-mode-map "M-s" t)
+ (keymap-unset paredit-mode-map "M-?" t)))
+
+(esy/init-step sweep
+ "Configure Prolog integration via `sweeprolog'."
+ (with-eval-after-load 'sweeprolog
+ (add-hook 'sweeprolog-mode-hook #'sweeprolog-electric-layout-mode)
+ (add-hook 'sweeprolog-top-level-mode-hook #'compilation-shell-minor-mode)
+ (keymap-global-set "C-c p" sweeprolog-prefix-map)))
+
+(esy/init-step rg
+ "Configure recursive grepping via `rg'."
+ (with-eval-after-load 'rg
+ (add-to-list 'rg-custom-type-aliases '("Prolog" . "*.pl *.plt *.pro *.prolog"))))
+
+(esy/init-step terraform
+ "Configure custom faces in `terraform-mode'."
+ (with-eval-after-load 'terraform-mode
+ (setq terraform--resource-name-face font-lock-function-name-face
+ terraform--resource-type-face font-lock-type-face)))
+
+(esy/init-step auto-mode
+ "Associate major modes with files based on their extensions."
+ (dolist (cell '(("Dockerfile" . dockerfile-ts-mode)
+ ("\\.ya?ml\\'" . yaml-ts-mode)
+ ("\\.json\\'" . json-ts-mode)
+ ("\\.rb\\'" . ruby-ts-mode)
+ ("\\.rs\\'" . rust-ts-mode)
+ ("\\.go\\'" . go-ts-mode)
+ ("\\.pdf\\'" . pdf-view-mode)
+ ("\\.plt?\\'" . sweeprolog-mode)))
+ (push cell auto-mode-alist)))
+
+(esy/init-step kill-hook
+ "Compile this file (if changed) when Emacs is killed."
+ (defun esy/compile-config ()
+ (interactive)
+ (when (file-newer-than-file-p user-init-file
+ (byte-compile-dest-file user-init-file))
+ (byte-compile-file user-init-file))
+ (when (file-newer-than-file-p early-init-file
+ (byte-compile-dest-file early-init-file))
+ (byte-compile-file early-init-file)))
+
+ (add-hook 'kill-emacs-hook #'esy/compile-config 10))
+
+(dolist (step (sort esy/init-step-times (lambda (l r) (time-less-p (cdr r) (cdr l)))))
+ (message "%f %s"
+ (float-time (cdr step))
+ (car step)))
(provide 'init)
;;; init.el ends here