+++ /dev/null
-#+TITLE: GNU Emacs literate configuration
-#+AUTHOR: Eshel Yaron
-#+EMAIL: eshelshay.yaron@gmail.com
-#+DESCRIPTION: Personal GNU Emacs configuration of Eshel Yaron
-#+KEYWORDS: eshel emacs configuration literate babel org elisp
-#+OPTIONS: ^:{}
-#+STARTUP: overview indent
-
-* Introduction
-:PROPERTIES:
-:CUSTOM_ID: introduction
-:END:
-
-This document holds my customizations for GNU Emacs. Its source
-version is written in [[https://orgmode.org/][Org mode]], utilizing [[https://orgmode.org/worg/org-contrib/babel/][Babel]] to realize [[https://en.wikipedia.org/wiki/Literate_programming][literate
-programming]]. The Elisp code blocks scattered throughout this
-document are bundled together to create an Elisp library called
-=esy.el=, which Emacs executes on startup.
-
-The source of this document is managed with Git in [[https://git.sr.ht/~eshel/dotfiles][my dotfiles
-repository hosted on SourceHut]]. An [[https://eshelyaron.com/esy.html][online HTML version of this Emacs
-configuration]] is also published on my website.
-
-** Last modification time
-:PROPERTIES:
-:CUSTOM_ID: last-modification-time
-:END:
-
-This file was last updated at:
-
-#+begin_src emacs-lisp :tangle no :exports results
- (format-time-string
- "%d-%m-%Y"
- (file-attribute-modification-time
- (file-attributes
- esy/source-path)))
-#+end_src
-
-** Current source control revision
-:PROPERTIES:
-:CUSTOM_ID: current-git-revision
-:END:
-
-Git revision of this file:
-
-#+begin_src emacs-lisp :tangle no :exports results
- (vc-git-working-revision esy/source-path)
-#+end_src
-
-* Fresh installation
-:PROPERTIES:
-:CUSTOM_ID: install
-:END:
-
-To bootstrap this configuration, fetch a local clone of the repository from
-[[https://git.sr.ht/~eshel/dotfiles][SourceHut]] and create a symlink from the =.emacs.d= subdirectory into your home
-directory, possibly using [[https://www.gnu.org/software/stow/][GNU Stow]].
-
-#+begin_src shell
- $ git clone https://git.sr.ht/~eshel/dotfiles
- $ stow -t ~ dotfiles/.emacs.d
- $ emacs
-#+end_src
-
-After the first run of the provided =init.el=, modifications to
-=esy.org= will be made available automatically whenever Emacs
-restarts. See also [[#bootstrap][Literate config bootstrap]].
-
-* Elisp Header
-:PROPERTIES:
-:CUSTOM_ID: header
-:END:
-
-For further information about Elisp headers, see [[info:elisp#Library
- Headers][elisp#Library Headers]].
-
-#+begin_src emacs-lisp
- ;;; esy.el --- GNU 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/>.
-
- ;;; Package-Requires: ((emacs "30"))
- ;;; Commentary:
- ;; Tangled version of esy.org
- ;;; Code:
-#+end_src
-
-* Allow for more memory usage during initialization
-:PROPERTIES:
-:CUSTOM_ID: garbage-collection-tweeks
-:END:
-
-#+begin_src emacs-lisp
- (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))))
-#+end_src
-
-* Suppressing compilation warnings
-:PROPERTIES:
-:CUSTOM_ID: compilation-warning
-:END:
-
-#+begin_src emacs-lisp
- (setq native-comp-async-report-warnings-errors 'silent)
- (setq warning-minimum-level :error)
-#+end_src
-
-* Package archives
-:PROPERTIES:
-:CUSTOM_ID: package-archives
-:END:
-
-#+begin_src emacs-lisp
- (require 'package)
-
- (add-to-list 'package-archives
- '("melpa" . "http://melpa.org/packages/"))
- (add-to-list 'package-archives
- '("elpa-devel" . "https://elpa.gnu.org/devel/"))
-
- (setq package-archive-column-width 12
- package-version-column-width 28)
-#+end_src
-
-* Selected packages
-:PROPERTIES:
-:CUSTOM_ID: selected-packages
-:END:
-
-These are external Emacs packages that I want to install:
-
-#+begin_src emacs-lisp
- (setq 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
- ))
-#+end_src
-
-Ensure they're all installed:
-
-#+begin_src emacs-lisp
- (package-install-selected-packages)
-#+end_src
-
-* No littering!
-:PROPERTIES:
-:CUSTOM_ID: no-littering
-:END:
-
-#+begin_src emacs-lisp
- (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))))
-#+end_src
-
-* Add local Elisp directory to load-path
-:PROPERTIES:
-:CUSTOM_ID: add-to-load-path
-:END:
-
-#+begin_src emacs-lisp
- (add-to-list 'load-path (expand-file-name "lisp/" user-emacs-directory))
-#+end_src
-
-* Display settings
-:PROPERTIES:
-:CUSTOM_ID: display
-:END:
-
-#+begin_src emacs-lisp
- (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)
-
- (setq inhibit-startup-screen t)
- (setq initial-scratch-message ";; Go.\n")
-
- (setq ring-bell-function 'ignore)
-
- (setq 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)
-
- (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)))
-#+end_src
-
-* History
-:PROPERTIES:
-:CUSTOM_ID: history
-:END:
-
-#+begin_src emacs-lisp
- (recentf-mode 1)
-
- (save-place-mode 1)
-
- (setq bookmark-save-flag 1)
-
- (savehist-mode 1)
- (with-eval-after-load 'log-edit
- (add-to-list 'savehist-additional-variables
- 'log-edit-comment-ring))
-#+end_src
-
-* Org-mode settings
-:PROPERTIES:
-:CUSTOM_ID: org
-:END:
-
-** Literate config bootstrap
-:PROPERTIES:
-:CUSTOM_ID: bootstrap
-:END:
-
-#+begin_src emacs-lisp
- (defconst esy/source-path (locate-user-emacs-file "esy.org")
- "Path to the Org version of this file.")
-
- (defconst esy/target-path (locate-user-emacs-file "esy.el")
- "Path to the Elisp version of this file.")
-
- (defun esy/tangle-and-compile-config ()
- "Tangle literate configuration file."
- (interactive)
- (when (file-newer-than-file-p esy/source-path esy/target-path)
- (require 'org)
- (require 'ob)
- (org-babel-tangle-file esy/source-path
- esy/target-path
- (rx string-start
- (or "emacs-lisp" "elisp")
- string-end))
- (byte-compile-file esy/target-path)))
-
- (add-hook 'kill-emacs-hook #'esy/tangle-and-compile-config)
-
- (defun esy/find-esy-org ()
- "Open my Emacs configuration."
- (interactive)
- (find-file esy/source-path))
-#+end_src
-
-** Org-mode basic settings
-:PROPERTIES:
-:CUSTOM_ID: org-must-haves
-:END:
-
-#+begin_src emacs-lisp
- (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")))
-#+end_src
-
-*** Always open files with =C-c C-o= inside Emacs
-:PROPERTIES:
-:CUSTOM_ID: org-file-apps-t-emacs
-:CreatedAt: <2022-06-10 Fri>
-:CapturedAt:
-:CapturedAs: Emacs configuration fragment
-:END:
-
-#+begin_src emacs-lisp
- (setq org-file-apps '((t . emacs)))
-#+end_src
-
-** Refile targets
-:PROPERTIES:
-:CUSTOM_ID: org-refile-targets
-:END:
-
-By default, =org-refile= considers only top level heading to be
-candidates for refiling into, we set =org-refile-targets= to allow
-refiling directly into deeper headings as well.
-
-#+begin_src emacs-lisp
- (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))
-#+end_src
-
-** Interactively fill missing CUSTOM_ID properties :cmd:
-:PROPERTIES:
-:CUSTOM_ID: orgs-custom-ids
-:END:
-
-#+begin_src emacs-lisp
- (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)))))
-#+end_src
-
-** Org-mode capture templates
-:PROPERTIES:
-:CUSTOM_ID: org-capture-templates
-:END:
-
-#+begin_src emacs-lisp
- (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
- :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:
- :PROPERTIES:
- :CreatedAt: %u
- :CapturedAt: %a
- :CapturedAs: Work Task
- :END:"
- :prepend t
- :empty-lines 1
- :immediate-finish t)
- ("e" "Emacs configuration fragment" entry
- (file+headline esy/source-path
- "Misc. settings")
- "** %^{Fragment} %^g
- :PROPERTIES:
- :CUSTOM_ID: %^{CUSTOM_ID}
- :CreatedAt: %u
- :CapturedAt: %a
- :CapturedAs: Emacs configuration fragment
- :END:\n\n#+begin_src emacs-lisp\n %i\n#+end_src"
- :empty-lines 1)
- ("c" "New Calendar Event" entry
- (file+headline esy/inbox-path "Calendar")
- "** %^{Title} %^g
- :PROPERTIES:
- :CreatedAt: %u
- :CapturedAt: %a
- :CapturedAs: Calendar Event
- :END:
- %(format-time-string \"<%Y-%m-%d %H:%M\" (org-read-date t t))-%(format-time-string \"%H:%M>\" (org-read-date t t))
- %i"
- :prepend t
- :empty-lines 1
- :immediate-finish t)
- ("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))))
-#+end_src
-
-** Open files (with =C-c C-o=) in Emacs
- :PROPERTIES:
- :CUSTOM_ID: org-file-apps-t-emacs
- :CreatedAt: <2022-06-10 Fri>
- :CapturedAt:
- :CapturedAs: Emacs configuration fragment
- :END:
-
-#+begin_src emacs-lisp
- (setq org-file-apps '((t . emacs)))
-#+end_src
-
-* Email settings
-:PROPERTIES:
-:CUSTOM_ID: email
-:END:
-
-** My accounts
-:PROPERTIES:
-:CUSTOM_ID: accounts
-:END:
-
-#+begin_src emacs-lisp
- (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-dazz "eshel@dazz.io"
- "My Dazz email address.")
-
- (defconst esy/user-mail-address-me "me@eshelyaron.com"
- "My persomal email address.")
-#+end_src
-
-** Sending mail from multiple SMTP accounts
-:PROPERTIES:
-:CUSTOM_ID: multiple-smtp-accounts
-:END:
-
-#+begin_src emacs-lisp
- (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)))
-
- (defun esy/customize-message-mode ()
- "Configure `message-mode' specific customizations."
- (require 'smtpmail-multi)
- (setq smtpmail-multi-accounts
- `((daz . ,(esy/smtpmail-multi-make-accout
- esy/user-mail-address-dazz
- "smtp.gmail.com"))
- (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"))))
- (setq smtpmail-multi-associations
- `((,(esy/smtpmail-multi-make-rx esy/user-mail-address-dazz) daz)
- (,(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)))
- (setq send-mail-function #'smtpmail-multi-send-it)
- (setq message-send-mail-function #'smtpmail-multi-send-it))
-
- (add-hook 'message-mode-hook #'esy/customize-message-mode)
- (add-hook 'mail-mode-hook #'esy/customize-message-mode)
-#+end_src
-
-** Reading mail with Gnus
-:PROPERTIES:
-:CUSTOM_ID: gnus
-:END:
-
-#+begin_src emacs-lisp
- (setq 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"
- (nnimap-address "mail.eshelyaron.com")
- (nnimap-server-port "imaps")
- (nnimap-stream ssl)
- (nnimap-authinfo-file "~/.authinfo")))
- gnus-no-groups-message "No new articles")
-
- (defun esy/customize-gnus-mode ()
- "Configure Gnus specific customizations."
- (require 'gnus)
- (require 'gnus-icalendar)
- (setq
- 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)
- (gnus-icalendar-setup)
- (gnus-icalendar-org-setup)
- (add-hook 'gnus-group-mode-hook #'gnus-topic-mode))
-
- (add-hook 'gnus-mode-hook #'esy/customize-gnus-mode)
-
- (with-eval-after-load 'gnus
- (require 'all-the-icons-gnus)
- (all-the-icons-gnus-setup))
-#+end_src
-
-* Global keybindings :kbd:
-:PROPERTIES:
-:CUSTOM_ID: global-keybindings
-:END:
-
-** =C-c= keybindings
-:PROPERTIES:
-:CUSTOM_ID: ctrl-c-bindings
-:END:
-
-#+begin_src emacs-lisp
- (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 S" #'esy/vterm-at)
- (keymap-global-set "C-c V" #'vertalen-at-point)
- (keymap-global-set "C-c F" #'esy/find-esy-org)
-
-#+end_src
-
-** Custom kill command :cmd:
-:PROPERTIES:
-:CUSTOM_ID: kill-dwim-command
-:END:
-
-#+begin_src emacs-lisp
- (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))))
-#+end_src
-
-** Pulse current line :cmd:
-:PROPERTIES:
-:CUSTOM_ID: pulse-line
-:END:
-
-#+begin_src emacs-lisp
- (defun esy/pulse-line (&optional _)
- "Pulse current line."
- (interactive)
- (pulse-momentary-highlight-one-line))
-#+end_src
-
-** Misc. keybindings
-:PROPERTIES:
-:CUSTOM_ID: misc-keybindings
-:END:
-
-#+begin_src emacs-lisp
- (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")
- (global-set-key [remap kill-region] #'esy/kill-dwim)
- (global-set-key [remap goto-line] #'consult-goto-line)
- (global-set-key [remap suspend-frame] #'zap-up-to-char)
- (global-set-key [remap imenu] #'consult-imenu)
- (global-set-key [remap make-frame] #'move-dup-duplicate-down)
- (global-set-key [remap ns-print-buffer] #'move-dup-duplicate-up)
- (global-set-key (kbd "M-#") #'define-word-at-point)
- (global-set-key (kbd "M-o") #'previous-buffer)
- (global-set-key (kbd "M-O") #'next-buffer)
- (global-set-key (kbd "C-,") #'backward-delete-char)
- (global-set-key (kbd "C-.") #'embark-act)
- (global-set-key (kbd "C-;") #'avy-goto-char-timer)
- (global-set-key (kbd "C-s-f") #'toggle-frame-fullscreen)
- (global-set-key (kbd "C-s-l") #'esy/pulse-line)
-#+end_src
-
-** =C-x= keybindings
-:PROPERTIES:
-:CUSTOM_ID: c-x-keybindings
-:END:
-
-#+begin_src emacs-lisp
- (keymap-set ctl-x-map "b" #'consult-buffer)
- (keymap-set ctl-x-4-map "b" #'consult-buffer-other-window)
- (keymap-set ctl-x-map "C-b" #'ibuffer)
-
- (put 'set-goal-column 'disabled nil)
- (put 'narrow-to-region 'disabled nil)
- (put 'narrow-to-page 'disabled nil)
- (put 'suspend-frame 'disabled t)
-#+end_src
-
-** =M-s= keybindings
-:PROPERTIES:
-:CUSTOM_ID: search-map-keybindings
-:END:
-
-#+begin_src emacs-lisp
- (keymap-set search-map "r" #'rg)
- (keymap-set search-map "l" #'rg-literal)
- (keymap-set search-map "b" #'some-button)
-#+end_src
-
-* Applications
-:PROPERTIES:
-:CUSTOM_ID: applications
-:END:
-
-** Magit
-
-#+begin_src emacs-lisp
- (with-eval-after-load 'magit
- (setq magit-repository-directories '(("~/checkouts/" . 1))))
-#+end_src
-
-** Mastodon
-
-#+begin_src emacs-lisp
- (with-eval-after-load 'mastodon
- (setq mastodon-instance-url "https://emacs.ch"
- mastodon-active-user "eshel"))
-#+end_src
-
-** tramp
-:PROPERTIES:
-:CUSTOM_ID: tramp-optimization
-:END:
-#+begin_src emacs-lisp
- (with-eval-after-load 'tramp
- (tramp-set-completion-function "ssh" '((tramp-parse-netrc "~/.authinfo"))))
-
- (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)))
-
- (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))))
-#+end_src
-
-** Dired
-:PROPERTIES:
-:CUSTOM_ID: dired
-:END:
-
-#+begin_src emacs-lisp
- (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)
- (setq dired-dwim-target t)
- (add-hook 'dired-mode-hook #'esy/local-all-the-icons-dired-mode))
-#+end_src
-
-** vterm :cmd:
-:PROPERTIES:
-:CUSTOM_ID: remote-vtrem
-:END:
-Start =vterm= in a given directory and with a given shell, possibly
-over ssh for remote connections.
-
-#+begin_src emacs-lisp
- (defun esy/vterm-mode-hook-function ()
- (setq-local term-prompt-regexp "^[^#$%>\n]*[#$%>] *"))
-
- (defun esy/vterm-with (arg shell)
- (interactive
- (list current-prefix-arg
- (completing-read
- "Start vterm with shell: "
- '("bash" "zsh" "sh")
- nil t nil nil "bash")))
- (require 'vterm)
- (let ((vterm-shell (concat "/bin/" shell)))
- (vterm arg)))
-
- (defun esy/vterm-at (arg dir)
- (interactive
- (list current-prefix-arg
- (read-directory-name "Start vterm in directory: " )))
- (let ((default-directory dir))
- (vterm arg)))
-
- (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))
-#+end_src
-
-** Elfeed
-:PROPERTIES:
-:CUSTOM_ID: elfeed
-:END:
-
-#+begin_src emacs-lisp
- (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"
- )))
-#+end_src
-
-** denote
-
-#+begin_src emacs-lisp
- (setq denote-directory "~/Notes")
-#+end_src
-** eww :www:
-:PROPERTIES:
-:CUSTOM_ID: eww
-:END:
-
-#+begin_src emacs-lisp
- (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")))
-
- ;; (with-eval-after-load 'shr
- ;; (setq shr-use-colors nil))
-#+end_src
-
-*** Prompt for URL with history-based completion
-:PROPERTIES:
-:CUSTOM_ID: eww-completing-read-url
-:CreatedAt: <2022-05-08 Sun>
-:CapturedAt: [[file:~/checkouts/protfiles/emacs/.emacs.d/prot-lisp/prot-eww.el::defun prot-eww-browse-dwim]]
-:CapturedAs: Emacs configuration fragment
-:END:
-
-The =eww= command in the heart of =eww.el= prompts the user for a URL,
-and browses it (see [[info:eww#Basics][eww#Basics]] for more details). One shortcoming of
-the built-in =eww= command is that it uses the =read-string= function
-to read the requested URL, which does not facilitate completions.
-
-The following fragment, inspired by [[https://protesilaos.com/emacs/dotemacs#h:524bc702-ff55-4ed9-9a38-26d30d64591d][Protesilaos Stavrou's prot-eww.el]],
-provides a simple wrapper for =eww= that uses =completing-read=
-instead of =read-string=, allowing for quick input completion based on
-the prior submitted URLs and web search keywords.
-
-#+begin_src emacs-lisp
- (defun esy/eww ()
- "Prompt for a URL or keywords to search the web for."
- (interactive)
- (require 'eww)
- (eww (mapconcat #'identity
- (completing-read-multiple "Browse or search: "
- eww-prompt-history
- nil nil nil
- 'eww-prompt-history
- (car (eww-suggested-uris)))
- " ")))
-#+end_src
-
-*** Use =eww= as the default web browser
-:PROPERTIES:
-:CUSTOM_ID: eww-browse-url-browser-function
-:CreatedAt: <2022-05-09 Mon>
-:CapturedAt: [[file:~/checkouts/emacs/nextstep/Emacs.app/Contents/Resources/lisp/net/browse-url.el.gz::setq browse-url-browser-function 'browse-url-w3]]
-:CapturedAs: Emacs configuration fragment
-:END:
-
-#+begin_src emacs-lisp
- (with-eval-after-load 'browse-url
- (setq browse-url-browser-function #'eww-browse-url))
-#+end_src
-
-** Proced
-:PROPERTIES:
-:CUSTOM_ID: proced-auto-update
-:CreatedAt: <2022-05-07 Sat>
-:CapturedAt: [[file:~/checkouts/protfiles/emacs/.emacs.d/prot-emacs.org::#h:6b56ce11-c84c-4b6d-98c7-bc3eefbe9325][file:~/checkouts/protfiles/emacs/.emacs.d/prot-emacs.org::#h:6b56ce11-c84c-4b6d-98c7-bc3eefbe9325]]
-:CapturedAs: Emacs configuration fragment
-:END:
-
-=proced.el= is an Elisp library built into Emacs that provides a
-listing of the currently running system processes. The following code
-fragment hooks =proced= to set =proced-auto-update-flag= variable to
-=t= on startup, making =M-x proced= behave similarly to how =top(1)=
-does in the shell.
-
-#+begin_src emacs-lisp
- (defun esy/setup-proced ()
- "Setup `proced-mode' specific settings."
- (setq proced-auto-update-flag t))
-
- (add-hook 'proced-mode-hook #'esy/setup-proced)
-#+end_src
-
-** BBDB
-:PROPERTIES:
-:CUSTOM_ID: bbdb-phone-style-nil
-:END:
-
-#+begin_src emacs-lisp
- (with-eval-after-load 'bbdb
- (setq bbdb-phone-style nil))
-
- (with-eval-after-load 'gnus
- (bbdb-initialize 'gnus 'mail 'message))
-#+end_src
-
-** Slack
-:PROPERTIES:
-:CUSTOM_ID: slack
-:END:
-#+begin_src emacs-lisp
- (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))
-#+end_src
-
-** EMMS
-:PROPERTIES:
-:CUSTOM_ID: emms
-:END:
-
-#+begin_src emacs-lisp
- (emms-minimalistic)
- (setq emms-player-list '(emms-player-mpv))
-#+end_src
-
-* Dutch to English translation with =define-word= and [[https://www.vertalen.nu/][vertalen.nu]]
-:PROPERTIES:
-:CUSTOM_ID: vertalen
-:END:
-
-#+begin_src emacs-lisp
- (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))))))
-#+end_src
-
-* Minibuffer and completions
-:PROPERTIES:
-:CUSTOM_ID: minibuffer-and-completions
-:END:
-
-** Enable and indicate recursive minibuffers
-:PROPERTIES:
-:CUSTOM_ID: recursive-minibuffers
-:END:
-
-#+begin_src emacs-lisp
- (setq enable-recursive-minibuffers t)
- (minibuffer-depth-indicate-mode)
-#+end_src
-
-** Completions
-:PROPERTIES:
-:CUSTOM_ID: completions
-:END:
-
-*** Completion at point
-:PROPERTIES:
-:CUSTOM_ID: completion-at-point
-:END:
-
-#+begin_src emacs-lisp
- (defun esy/dabbrev-capf ()
- "Workaround for issue with `dabbrev-capf'."
- (require 'dabbrev)
- (dabbrev--reset-global-variables)
- (setq dabbrev-case-fold-search nil)
- (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))))))
-
- (defun esy/margin-formatter (metadata)
- "Margin formatter 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 (_) "… "))))
-
- (setq corfu-cycle t
- corfu-margin-formatters '(esy/margin-formatter)
- corfu-indexed-start 1)
- (global-corfu-mode)
- (corfu-indexed-mode 1)
- (add-to-list 'completion-at-point-functions #'esy/dabbrev-capf)
- (add-to-list 'completion-at-point-functions #'esy/file-capf)
-#+end_src
-
-*** Minibuffer-based completions
-:PROPERTIES:
-:CUSTOM_ID: minibuffer-completion
-:END:
-
-#+begin_src emacs-lisp
- (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))
-#+end_src
-
-* Mode-line customizations
-:PROPERTIES:
-:CUSTOM_ID: modeline
-:END:
-
-#+begin_src emacs-lisp
- (setq display-time-mail-function (lambda () nil))
- (display-time-mode)
- (column-number-mode)
- (display-battery-mode)
- #+end_src
-
-* Text mode and derivatives
-:PROPERTIES:
-:CUSTOM_ID: text-mode
-:END:
-#+begin_src emacs-lisp
- (add-hook 'text-mode-hook #'flyspell-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))
-#+end_src
-
-* LaTeX and PDF settings
-:PROPERTIES:
-:CUSTOM_ID: latex-and-pdf
-:END:
-
-#+begin_src emacs-lisp
- (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))
-#+end_src
-
-* Programming
-:PROPERTIES:
-:CUSTOM_ID: programming
-:END:
-
-** Compilation
-:PROPERTIES:
-:CUSTOM_ID: compilation
-:END:
-
-#+begin_src emacs-lisp
- (setq safe-local-variable-values
- '((compilation-read-command . nil)))
-#+end_src
-
-** General =prog-mode= settings
-:PROPERTIES:
-:CUSTOM_ID: prog-mode
-:END:
-
-#+begin_src emacs-lisp
- (defun esy/setup-programming ()
- "Setup `prog-mode' and more programming-related settings."
- (require 'rainbow-delimiters)
- (require 'flymake)
- (rainbow-delimiters-mode)
- (display-line-numbers-mode)
- (display-fill-column-indicator-mode)
- (flymake-mode))
-
- (add-hook 'prog-mode-hook #'esy/setup-programming)
-#+end_src
-
-** Lisp specific settings
-:PROPERTIES:
-:CUSTOM_ID: lisp
-:END:
-
-*** Paredit
-:PROPERTIES:
-:CUSTOM_ID: paredit
-:END:
-
-Enable =paredit-mode= in =lisp-data-mode= and its derivatites, which
-include =emacs-lisp-mode= and =lisp-interaction-mode=.
-
-#+begin_src emacs-lisp
- (defun esy/setup-lisp ()
- "Setup Lisp specific settings."
- (require 'paredit)
- (enable-paredit-mode))
-
- (add-hook 'lisp-data-mode-hook #'esy/setup-lisp)
-#+end_src
-
-** Haskell specific settings
-:PROPERTIES:
-:CUSTOM_ID: haskell
-:END:
-
-#+begin_src emacs-lisp
- (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)
-#+end_src
-
-** Prolog specific settings
-:PROPERTIES:
-:CUSTOM_ID: prolog
-:END:
-
-*** Setup =sweep=
-:PROPERTIES:
-:CUSTOM_ID: setup-sweep
-:END:
-
-#+begin_src emacs-lisp
- (require 'use-package)
-
- (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))
-#+end_src
-
-*** Make =rg= regard =.pl= files as Prolog rather than Perl
-:PROPERTIES:
-:CUSTOM_ID: rg-pl-is-prolog-not-perl
-:END:
-
-#+begin_src emacs-lisp
- (with-eval-after-load 'rg
- (add-to-list 'rg-custom-type-aliases '("Prolog" . "*.pl *.plt *.pro *.prolog")))
-#+end_src
-
-** Dockerfile
-:PROPERTIES:
-:CUSTOM_ID: dockerfile
-:END:
-
-#+begin_src emacs-lisp
- (add-to-list 'auto-mode-alist '("Dockerfile" . dockerfile-ts-mode))
-#+end_src
-
-** YAML
-:PROPERTIES:
-:CUSTOM_ID: yaml
-:END:
-
-#+begin_src emacs-lisp
- (add-to-list 'auto-mode-alist '("\\.ya?ml\\'" . yaml-ts-mode))
-#+end_src
-
-** JSON
-:PROPERTIES:
-:CUSTOM_ID: json
-:END:
-
-#+begin_src emacs-lisp
- (add-to-list 'auto-mode-alist '("\\.json\\'" . json-ts-mode))
-#+end_src
-
-** Rust
-:PROPERTIES:
-:CUSTOM_ID: rust
-:END:
-
-#+begin_src emacs-lisp
- (add-to-list 'auto-mode-alist '("\\.rs\\'" . rust-ts-mode))
-#+end_src
-
-** Go
-:PROPERTIES:
-:CUSTOM_ID: go
-:END:
-
-#+begin_src emacs-lisp
- (add-to-list 'auto-mode-alist '("\\.go\\'" . go-ts-mode))
-#+end_src
-
-** Terraform
-:PROPERTIES:
-:CUSTOM_ID: terraform
-:END:
-
-#+begin_src emacs-lisp
- (use-package terraform-mode
- :config
- (setq terraform--resource-name-face font-lock-function-name-face
- terraform--resource-type-face font-lock-type-face))
-#+end_src
-
-* =vc= customizations
-:PROPERTIES:
-:CUSTOM_ID: vc-follow-symlinks-t
-:END:
-
-By default, Emacs asks for confirmation when visiting a path that
-points to symlink to a file committed to version control. I often
-visit this file with =C-x C-f ~/.emacs.d/esy.org=, which is a symlink
-to the actual source of this file which is managed by git and Stow, so
-in this cases I prefer Emacs to trust me that I know what I'm doing
-without asking each time.
-
-#+begin_src emacs-lisp
- (setq vc-follow-symlinks t)
-#+end_src
-
-* Project management
-:PROPERTIES:
-:CUSTOM_ID: project-management
-:END:
-** Populate project list from my projects directory
-:PROPERTIES:
-:CUSTOM_ID: remember-projects-under-directory
-:END:
-
-#+begin_src emacs-lisp
- (defconst esy/projects-directory "~/checkouts/"
- "Path of the projects directory.")
-
- (add-hook 'kill-emacs-hook
- (lambda ()
- (require 'project)
- (mapcar #'project-remember-projects-under
- (directory-files
- esy/projects-directory))))
-#+end_src
-
-** Project switch commands
-:PROPERTIES:
-:CUSTOM_ID: project-switch-commands
-:END:
-
-#+begin_src emacs-lisp
- (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)))
-#+end_src
-
-* =shell= customizations
-:PROPERTIES:
-:CUSTOM_ID: shell
-:END:
-
-** Kill =shell= buffers on exit
-:PROPERTIES:
-:CUSTOM_ID: shell-kill-on-exit
-:END:
-Kill =M-x shell= buffers automatically when the shell process
-terminates, e.g. when pressing =C-d=.
-
-#+begin_src emacs-lisp
- (setq shell-kill-buffer-on-exit t)
-#+end_src
-
-** Restart async shell process :cmd:kbd:
-:PROPERTIES:
-:CUSTOM_ID: shell-restart-process
-:CreatedAt: <2022-07-01 Fri>
-:CapturedAt:
-:CapturedAs: Emacs configuration fragment
-:END:
-
-#+begin_src emacs-lisp
- (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))
-#+end_src
-
-** Save logs of =comint= buffers :cmd:kbd:
-:PROPERTIES:
-:CUSTOM_ID: comint-log-buffer
-:CreatedAt: <2022-07-13 Wed>
-:CapturedAt:
-:CapturedAs: Emacs configuration fragment
-:END:
-
-#+begin_src emacs-lisp
- (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))
-#+end_src
-
-* Misc. settings
-:PROPERTIES:
-:CUSTOM_ID: misc
-:END:
-
-#+begin_src emacs-lisp
- (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)
-#+end_src
-
-** Enable ~embark-consult~
-
-This is a little companion library for ~embark~ that makes ~embark-export~
-and friends work correctly with ~consult~ enabled completions.
-
-#+begin_src emacs-lisp
- (with-eval-after-load 'consult
- (with-eval-after-load 'embark
- (require 'embark-consult)))
-#+end_src
-
-** Add a repeat-map to =tranpose-lines=
-:PROPERTIES:
-:CUSTOM_ID: repeat-transpose-lines
-:END:
-
-#+begin_src emacs-lisp
- (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)
-#+end_src
-
-** Use =consult= to show =xref= results
-:PROPERTIES:
-:CUSTOM_ID: consult-xref
-:END:
-
-#+begin_src emacs-lisp
- (with-eval-after-load 'xref
- (setq xref-show-definitions-function #'consult-xref
- xref-show-xrefs-function #'consult-xref
- xref-search-program 'ripgrep))
-#+end_src
-
-** Show the time in Amsterdam in =world-clock=
-:PROPERTIES:
-:CUSTOM_ID: world-clock-amsterdam
-:CreatedAt: <2022-05-12 Thu>
-:CapturedAt: [[file:~/checkouts/dotfiles/.emacs.d/esy.org::#footer][file:~/checkouts/dotfiles/.emacs.d/esy.org::#footer]]
-:CapturedAs: Emacs configuration fragment
-:END:
-
-Add the timezones of places of interest to the list of clocks shown by
-=M-x world-clock=. The list of named timezones is maintained by [[https://www.iana.org/time-zones][IANA]].
-
-#+begin_src emacs-lisp
- (with-eval-after-load 'time
- (add-to-list 'zoneinfo-style-world-list '("Europe/Amsterdam" "Amsterdam")))
-#+end_src
-
-** Restart async shell process in =process-menu-mode= :cmd:kbd:
-:PROPERTIES:
-:CUSTOM_ID: process-menu-restart-process
-:CreatedAt: <2022-07-01 Fri>
-:CapturedAt:
-:CapturedAs: Emacs configuration fragment
-:END:
-
-#+begin_src emacs-lisp
- (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))
-#+end_src
-
-** Enable =repeat-mode=
-:PROPERTIES:
-:CUSTOM_ID: repeat-mode
-:END:
-
-#+begin_src emacs-lisp
- (repeat-mode)
-#+end_src
-
-** Sibling files
-:PROPERTIES:
-:CUSTOM_ID: find-sibling
-:END:
-#+begin_src emacs-lisp
- (setq find-sibling-rules '(("\\([^/]+\\)\\.c\\'" "\\1.h")))
-#+end_src
-
-** Small command for converting Unix timestamps to date strings
-
-#+begin_src emacs-lisp
- (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))))
-#+end_src
-
-
-** Predefined SQL connections
-:PROPERTIES:
-:CUSTOM_ID: predefined-sql-connections
-:CreatedAt: <2022-08-11 Thu>
-:CapturedAt: [[file:~/tmp/foo.el]]
-:CapturedAs: Emacs configuration fragment
-:END:
-
-#+begin_src emacs-lisp
- (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 "^"))))))))
-
- (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))))
-#+end_src
-
-* Elisp Footer
-:PROPERTIES:
-:CUSTOM_ID: footer
-:END:
-
-#+begin_src emacs-lisp
- (provide 'esy)
- ;;; esy.el ends here
-#+end_src
-;;; init.el --- Load the full configuration -*- lexical-binding: t -*-
+;;; 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/>.
+
;;; Commentary:
+;; My personal Emacs configuration
+
;;; Code:
-;; (require 'benchmark-init)
-;; (add-hook 'after-init-hook 'benchmark-init/deactivate)
+(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))))
-(let ((esy (expand-file-name (locate-user-emacs-file "esy"))))
- (or (load esy t t nil t)
- (progn
- (require 'org)
- (org-babel-load-file (concat esy ".org") t))))
+(require 'comp)
+(setq
+ ;; 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)
-(provide 'init)
-;;; init.el ends here
+(require 'package)
+(setq
+ ;; 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
+: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:
+: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
+:PROPERTIES:
+:CreatedAt: %u
+:CapturedAt: %a
+:CapturedAs: Calendar Event
+:END:
+%(format-time-string \"<%Y-%m-%d %H:%M\" (org-read-date t t))-%(format-time-string \"%H:%M>\" (org-read-date t t))
+%i"
+ :prepend t
+ :empty-lines 1
+ :immediate-finish t)
+ ("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"
+ (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)
+
+ (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/find-init-file (init)
+ (interactive (list user-init-file))
+ (find-file init))
+
+(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))
+
+(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 "<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-map "C-b" #'ibuffer)
+
+(keymap-set search-map "r" #'rg)
+(keymap-set search-map "l" #'rg-literal)
+(keymap-set search-map "b" #'some-button)
+
+(dolist (command '(set-goal-column
+ narrow-to-region
+ narrow-to-page))
+ (put command 'disabled nil))
+
+(put 'suspend-frame 'disabled t)
+
+(with-eval-after-load 'magit
+ (setq magit-repository-directories '(("~/checkouts/" . 1))))
+
+(with-eval-after-load 'mastodon
+ (setq mastodon-instance-url "https://emacs.ch"
+ mastodon-active-user "eshel"))
+
+(with-eval-after-load 'tramp
+ (tramp-set-completion-function "ssh" '((tramp-parse-netrc "~/.authinfo.gpg"))))
+
+(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)))
+
+(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))))
+
+(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)
+ (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 'browse-url
+ (setq browse-url-browser-function #'eww-browse-url))
+
+(add-hook 'proced-mode-hook #'proced-toggle-auto-update)
+
+(with-eval-after-load 'bbdb
+ (setq bbdb-phone-style nil))
+
+(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))
+
+(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))))))
+
+(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)
+
+(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 (_) "… "))))
+
+(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)
+
+(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))
+
+(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)
+
+(add-hook 'lisp-data-mode-hook #'enable-paredit-mode)
+
+(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)
+
+(require 'use-package)
+
+(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))
+
+(with-eval-after-load 'rg
+ (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 "^"))))))))
+
+ (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))))
+
+(provide 'init)
+;;; init.el ends here