From f8675f16850facf4a13c0f4ed702b312c88224bb Mon Sep 17 00:00:00 2001 From: Eshel Yaron Date: Wed, 15 Mar 2023 21:35:56 +0200 Subject: [PATCH] Remove esy.org - use init.el instead --- .emacs.d/esy.org | 1845 ---------------------------------------------- .emacs.d/init.el | 1053 +++++++++++++++++++++++++- .zshenv | 2 +- 3 files changed, 1044 insertions(+), 1856 deletions(-) delete mode 100644 .emacs.d/esy.org diff --git a/.emacs.d/esy.org b/.emacs.d/esy.org deleted file mode 100644 index 9c0dbb3..0000000 --- a/.emacs.d/esy.org +++ /dev/null @@ -1,1845 +0,0 @@ -#+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 - ;; 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 . - - ;;; 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 diff --git a/.emacs.d/init.el b/.emacs.d/init.el index e30eabf..960a665 100644 --- a/.emacs.d/init.el +++ b/.emacs.d/init.el @@ -1,18 +1,1051 @@ -;;; 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 +;; 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 . + ;;; 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 " " #'esy/kill-dwim) +(keymap-global-set " " #'consult-goto-line) +(keymap-global-set " " #'zap-up-to-char) +(keymap-global-set " " #'consult-imenu) +(keymap-global-set " " #'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 diff --git a/.zshenv b/.zshenv index 4c0038f..8d40f90 100644 --- a/.zshenv +++ b/.zshenv @@ -1 +1 @@ -export PATH=/Users/eshelyaron/checkouts/emacs/nextstep/Emacs.app/Contents/MacOS:/Users/eshelyaron/.cargo/bin:/Users/eshelyaron/go/bin:$PATH +export PATH="/Users/eshelyaron/.local/bin:/Users/eshelyaron/checkouts/emacs/nextstep/Emacs.app/Contents/MacOS:/Users/eshelyaron/.cargo/bin:/Users/eshelyaron/go/bin:$PATH" -- 2.39.2