From 04667840a285dabb50ee9cab0743061627a52c94 Mon Sep 17 00:00:00 2001 From: Eshel Yaron Date: Thu, 6 Apr 2023 16:54:34 +0300 Subject: [PATCH] Update Emacs config --- .emacs.d/early-init.el | 9 - .emacs.d/init.el | 1842 ++++++++++++++++++---------------- .emacs.d/lisp/esy-capture.el | 51 + 3 files changed, 1003 insertions(+), 899 deletions(-) create mode 100644 .emacs.d/lisp/esy-capture.el diff --git a/.emacs.d/early-init.el b/.emacs.d/early-init.el index 5bde9bd..5a555e7 100644 --- a/.emacs.d/early-init.el +++ b/.emacs.d/early-init.el @@ -3,17 +3,8 @@ ;;; Code: -(defvar esy/init-step-times (list (cons nil (current-time)))) - -(defun esy/init-step (step) - (push (cons step (current-time)) - esy/init-step-times)) - (tool-bar-mode -1) -(esy/init-step 'tool-bar-mode) - (set-scroll-bar-mode nil) -(esy/init-step 'scroll-bar-mode) (provide 'early-init) ;;; early-init.el ends here diff --git a/.emacs.d/init.el b/.emacs.d/init.el index 167c9e8..b45b1f0 100644 --- a/.emacs.d/init.el +++ b/.emacs.d/init.el @@ -9,130 +9,142 @@ ;;; Code: -(esy/init-step 'init) - -;;;; temporarly increase GC threshold to expedite Emacs startup -(let ((normal-gc-cons-threshold (* 20 1024 1024)) - (init-gc-cons-threshold (* 1024 1024 1024)) - (normal-mode-line-format mode-line-format)) - (setq gc-cons-threshold init-gc-cons-threshold - mode-line-format nil) - (add-hook 'after-init-hook - (lambda () (setq gc-cons-threshold normal-gc-cons-threshold - mode-line-format normal-mode-line-format)))) -(esy/init-step 'gc) - -(setq - ;; my name - user-full-name "Eshel Yaron" - ;; my email address - user-mail-address "me@eshelyaron.com" - ;; direct Custom definitions to some file and forget about it - custom-file (expand-file-name "custom.el" user-emacs-directory) - ;; silence native compilation warnings - native-comp-async-report-warnings-errors 'silent - ;; only display the warning buffer if something's really broken - warning-minimum-level :error - ;; don't use stale .elc files - load-prefer-newer t - ;; maximize Emacs on startup - initial-frame-alist '((fullscreen . fullboth)) - ;; disable popup dialogs - use-dialog-box nil - ;; disable splash screen - inhibit-startup-screen t - ;; make the scratch message more concise - initial-scratch-message ";; Go.\n" - ;; don't ring the bell - ring-bell-function #'ignore - ;; make C-x b obey display-buffer-alist - switch-to-buffer-obey-display-actions t - ;; disable new mail mode line indication - display-time-mail-function #'ignore - ;; enable recursive minibuffers - enable-recursive-minibuffers t - ;; save bookmarks immediately - bookmark-save-flag 1 - ;; make some space in the Packages menu - package-archive-column-width 12 - package-version-column-width 28 - ;; select some packages to install - package-selected-packages '( - all-the-icons - all-the-icons-completion - all-the-icons-dired - all-the-icons-gnus - auctex - auctex-latexmk - avy - bbdb - corfu - define-word - devdocs - diff-hl - elfeed - embark-consult - emms - gnu-elpa-keyring-update - gnuplot - graphql-mode - graphviz-dot-mode - haskell-mode - htmlize - ialign - keycast - kubernetes - lin - magit - marginalia - markdown-mode - mastodon - ob-prolog - orderless - org - org-modern - package-lint - paredit - pdf-tools - rainbow-delimiters - rainbow-mode - request - rg - smtpmail-multi - sqlformat - sweeprolog - terraform-mode - vterm - vundo - whitespace-cleanup-mode - with-editor - ) - ;; configure Org capture templates - org-capture-templates '(("t" "Todo [inbox]" entry - (file+headline "~/org/inbox.org" "Tasks") - "** TODO [#B] %^{Task} %^g +(defvar esy/init-step-times nil) + +(defmacro esy/init-step (name _doc &rest body) + (declare (indent defun) + (doc-string 2)) + (let ((start-time-symbol (gensym))) + `(let ((,start-time-symbol (current-time))) + ,@body + (push (cons ',name + (time-subtract (current-time) ,start-time-symbol)) + esy/init-step-times)))) + +(esy/init-step gc + "Temporarily increase GC threshold to expedite Emacs startup." + (let ((normal-gc-cons-threshold (* 20 1024 1024)) + (init-gc-cons-threshold (* 1024 1024 1024)) + (normal-mode-line-format mode-line-format)) + (setq gc-cons-threshold init-gc-cons-threshold + mode-line-format nil) + (add-hook 'after-init-hook + (lambda () (setq gc-cons-threshold normal-gc-cons-threshold + mode-line-format normal-mode-line-format))))) + +(esy/init-step vars + "Set some variables." + (setq + ;; my name + user-full-name "Eshel Yaron" + ;; my email address + user-mail-address "me@eshelyaron.com" + ;; direct Custom definitions to some file and forget about it + custom-file (expand-file-name "custom.el" user-emacs-directory) + ;; silence native compilation warnings + native-comp-async-report-warnings-errors 'silent + ;; only display the warning buffer if something's really broken + warning-minimum-level :error + ;; don't use stale .elc files + load-prefer-newer t + ;; maximize Emacs on startup + default-frame-alist '((fullscreen . fullboth)) + ;; disable popup dialogs + use-dialog-box nil + ;; disable splash screen + inhibit-startup-screen t + ;; make the scratch message more concise + initial-scratch-message ";; Go.\n" + ;; don't ring the bell + ring-bell-function #'ignore + ;; make C-x b obey display-buffer-alist + switch-to-buffer-obey-display-actions t + ;; disable new mail mode line indication + display-time-mail-function #'ignore + ;; enable recursive minibuffers + enable-recursive-minibuffers t + ;; save bookmarks immediately + bookmark-save-flag 1 + ;; make some space in the Packages menu + package-archive-column-width 12 + package-version-column-width 28 + ;; configure package archives + package-archives '(("melpa" . "http://melpa.org/packages/") + ("gnu" . "https://elpa.gnu.org/devel/") + ("nongnu" . "https://elpa.nongnu.org/nongnu-devel/")) + ;; select some packages to install + package-selected-packages '( + all-the-icons + all-the-icons-completion + all-the-icons-dired + all-the-icons-gnus + auctex + avy + bbdb + corfu + define-word + devdocs + diff-hl + elfeed + embark-consult + emms + gnu-elpa-keyring-update + gnuplot + graphql-mode + graphviz-dot-mode + htmlize + ialign + keycast + kubernetes + lin + magit + marginalia + markdown-mode + mastodon + ob-prolog + orderless + org + org-modern + package-lint + paredit + pdf-tools + rainbow-delimiters + rainbow-mode + rg + smtpmail-multi + sqlformat + sweeprolog + terraform-mode + vterm + vundo + whitespace-cleanup-mode + ) + ;; configure Org capture templates + org-capture-templates '(("t" "Todo [inbox]" entry + (file+headline "~/org/inbox.org" "Tasks") + "** TODO [#B] %^{Task} %^g :PROPERTIES: :CreatedAt: %u :CapturedAt: %a :CapturedAs: Inbox Task :END:" - :prepend t - :empty-lines 1 - :immediate-finish t) - ("w" "Work [inbox]" entry - (file+headline "~/org/inbox.org" "Tasks") - "** TODO [#B] %^{Task} :work: + :prepend t + :empty-lines 1 + :immediate-finish t) + ("w" "Work [inbox]" entry + (file+headline "~/org/inbox.org" "Tasks") + "** TODO [#B] %^{Task} :work: :PROPERTIES: :CreatedAt: %u :CapturedAt: %a :CapturedAs: Work Task :END:" - :prepend t - :empty-lines 1 - :immediate-finish t) - ("c" "New Calendar Event" entry - (file+headline "~/org/inbox.org" "Calendar") - "** %^{Title} %^g + :prepend t + :empty-lines 1 + :immediate-finish t) + ("c" "New Calendar Event" entry + (file+headline "~/org/inbox.org" "Calendar") + "** %^{Title} %^g :PROPERTIES: :CreatedAt: %u :CapturedAt: %a @@ -140,783 +152,833 @@ :END: %(format-time-string \"<%Y-%m-%d %H:%M\" (org-read-date t t))-%(format-time-string \"%H:%M>\" (org-read-date t t)) %i" - :prepend t - :empty-lines 1 - :immediate-finish t)) - ;; point Org to file agenda file - org-agenda-files '("~/org/inbox.org") - org-default-notes-file "~/org/inbox.org" - ;; weeks start on Sunday - org-agenda-start-on-weekday 0 - ;; use a nice unicode ellipsis - org-ellipsis "…" - ;; open everything withing Emacs - org-file-apps '((t . emacs)) - ;; enable Org speed commands - org-use-speed-commands t - ;; task states - org-todo-keywords '((sequence - "TODO(t)" - "BLOCKED(b@/!)" - "INPROGRESS(i!)" - "|" - "DONE(d!)" - "CANCELED(c@)")) - ;; set a task to INPROGRESS when I clock into it - org-clock-in-switch-to-state "INPROGRESS" - ;; log the finish time of tasks in Org - org-log-done 'time - org-log-into-drawer t - ;; minimal prompt for task states - org-use-fast-todo-selection 'expert - ;; set up a group of mutual exclusive tags - org-tag-alist '((:startgroup) - ("work" . ?w) - ("studies" . ?s) - ("esols" . ?e) - ("personal" . ?p) - (:endgroup)) - ;; minimal prompt for tags - org-fast-tag-selection-single-key 'expert - ;; allow evaluating some languages with Org Babel - org-babel-load-languages '((emacs-lisp . t) - (shell . t) - (sql . t) - (prolog . t)) - ;; do not ask for confirmation when evaluating Org source blocks - org-confirm-babel-evaluate nil - ;; extra modules to load along with Org - org-modules '(ol-bbdb - ol-bibtex - ol-docview - ol-gnus - ol-info - ol-irc - ol-mhe - ol-rmail - ol-eww - ob-sql - org-tempo) - ;; refile Org entries to my agenda file(s) - org-refile-targets '((org-agenda-files . (:maxlevel . 5)) - (nil . (:maxlevel . 3))) - ;; use full outline path when prompting for refile target - org-refile-use-outline-path t - ;; archive for Org entries - org-archive-location "~/org/journal.org::datetree/* Finished Tasks :ARCHIVE:" - ;; enable italic text in code - modus-themes-italic-constructs t - ;; enable bold text in code - modus-themes-bold-constructs t - ;; use fixed-pitch text where appropriate - modus-themes-mixed-fonts t - ;; make prompts bold - modus-themes-prompts '(bold) - ;; give source blocks a distinct background shade - modus-themes-org-blocks 'gray-background - ;; use variable-pitch text here are there - modus-themes-variable-pitch-ui t - ;; fine-tune some theme settings - modus-themes-common-palette-overrides '((border-mode-line-active unspecified) - (border-mode-line-inactive unspecified) - (bg-mode-line-active bg-blue-intense) - (fg-mode-line-active fg-main) - (fringe unspecified) - (underline-link border) - (underline-link-visited border) - (underline-link-symbolic border) - (fg-region unspecified)) - ;; follow links to version-controlled files without confirming - vc-follow-symlinks t - ;; jump from .c to .h files with find-sibling-file - find-sibling-rules '(("\\([^/]+\\)\\.c\\'" "\\1.h")) - ;; configure multiple SMTP accounts - smtpmail-multi-accounts '((esy . ("eshelshay.yaron@gmail.com" - "smtp.gmail.com" 587 - "eshelshay.yaron@gmail.com" - starttls nil nil nil)) - (swp . ("eshel@swi-prolog.org" - "mail.swi-prolog.com" 587 - "eshel@swi-prolog.org" - starttls nil nil nil)) - (me . ("me@eshelyaron.com" - "mail.eshelyaron.com" - 587 - "me@eshelyaron.com" - starttls nil nil nil))) - ;; associate email addresses with SMTP accounts - smtpmail-multi-associations '(("[^z-a]*eshelshay\\.yaron@gmail\\.com[^z-a]*" esy) - ("[^z-a]*eshel@swi-prolog\\.org[^z-a]*" swp) - ("[^z-a]*me@eshelyaron\\.com[^z-a]*" me)) - ;; set function for sending email - send-mail-function #'smtpmail-multi-send-it - message-send-mail-function #'smtpmail-multi-send-it - ;; set email user agent - mail-user-agent 'gnus-user-agent - ;; make Gnus not prompt as much - gnus-expert-user t - ;; make Gnus read the dribble file on startup without prompting - gnus-always-read-dribble-file t - ;; don't break pages in incoming email - gnus-break-pages nil - ;; don't show the Gnus startup screen - gnus-inhibit-startup-message t - ;; set Gnus backends - gnus-select-method '(nntp "news.gmane.io") - gnus-secondary-select-methods '((nnimap "gmail" - (nnimap-address "imap.gmail.com") - (nnimap-server-port "imaps") - (nnimap-stream ssl)) - (nnimap "me" - (nnimap-address "mail.eshelyaron.com") - (nnimap-server-port "imaps") - (nnimap-stream ssl)) - (nnimap "swi" - (nnimap-address "mail.swi-prolog.com") - (nnimap-server-port "imaps") - (nnimap-stream ssl))) - ;; simpler message - gnus-no-groups-message "No new articles" - ;; don't close other windows when opening Gnus - gnus-use-full-window nil - ;; make Gnus handle this content types - gnus-article-treat-types '("text/plain" - "text/x-verbatim" - "text/x-patch" - "text/html" - "text/calendar") - ;; capture calendar events from Gnus to my Org inbox file - gnus-icalendar-org-capture-file "~/org/inbox.org" - ;; put calendar events under the Calendar heading - gnus-icalendar-org-capture-headline '("Calendar") - ;; use ISO format for calendar dates - calendar-date-style 'iso - ;; maintain legibility of rendered text in HTML mails - shr-color-visible-luminance-min 75 - ;; my Mastodon settings - mastodon-instance-url "https://emacs.ch" - mastodon-active-user "eshel" - ;; direct Magit to my Git checkouts directory - magit-repository-directories '(("~/checkouts/" . 1)) - ;; have Dired operations target another visible Dired buffer - dired-dwim-target t - ;; use zsh in vterm by default - vterm-shell "/bin/zsh" - vterm-max-scrollback 2048 - vterm-kill-buffer-on-exit nil - vterm-use-vterm-prompt-detection-method t - ;; use MPV with EMMS - emms-player-list '(emms-player-mpv) - bbdb-phone-style nil - eww-auto-rename-buffer 'title - browse-url-browser-function #'eww-browse-url - browse-url-generic-program "open" - ;; set up some feeds for Elfeed - elfeed-feeds '( - ("http://node2.feed43.com/7487052648530856.xml" comics scifi) - ("https://ajroach42.com/feed.xml") - ("https://alexschroeder.ch/wiki/feed/full/" tech) - ("https://amodernist.com/all.atom" emacs) - ("https://archive.casouri.cc/note/atom.xml") - ("https://arcology.garden/updates.xml") - ("http://www.aaronsw.com/weblog/index.xml" blog tech) - ("https://atthis.link/rss.xml" tech) - ("https://daily.baty.net/feed/feed.xml" blog tech) - ("https://baty.net/feed" tech) - ("https://betterappsec.com/feed") - ("https://bitspook.in/blog/feed.xml") - ("https://blog.acthompson.net/feeds/posts/default") - ("https://bruda.ca/feed.php") - ("https://cce.whatthefuck.computer/updates.xml") - ("https://cdn.jwz.org/blog/feed/" tech) - ("https://cestlaz.github.io/rss.xml" tech) - ("https://changelog.complete.org/feed") - ("https://daniel.haxx.se/blog/feed/" tech) - ("https://drewdevault.com/blog/index.xml") - ("https://emacs.dyerdwelling.family/index.xml") - ("https://emacsninja.com/feed.atom" emacs) - ("https://erikmcclure.com/blog/index.xml") - ("https://evanhahn.com/blog/index.xml") - ("https://fasterthanli.me/index.xml") - ("https://feeds.buzzsprout.com/2134279.rss") - ("https://flower.codes/feed.xml") - ("https://garymarcus.substack.com/feed") - ("https://gwern.substack.com/feed" tech blog) - ("https://herman.bearblog.dev/feed/") - ("https://irreal.org/blog/?feed=rss2" emacs) - ("https://jcm.libsyn.com/rss/") - ("https://jnd.org/feed" tech philosophy) - ("https://kelar.org/~bandali/atom.xml" tech blog) - ("https://kitchen-sink.kwakk.info/feed" comics) - ("https://lars.ingebrigtsen.no/feed" emacs) - ("https://lwn.net/headlines/rss" linux) - ("https://maggieappleton.com/rss.xml" design) - ("https://matklad.github.io/feed.xml") - ("https://matt-rickard.com/rss" tech) - ("https://njoseph.me/shaarli/feed/atom?") - ("https://nullprogram.com/feed/" tech) - ("https://olddeuteronomy.github.io/index.xml") - ("https://parasurv.neocities.org/rss.xml" tech blog) - ("https://phaazon.net/blog/feed") - ("https://planet.emacslife.com/atom.xml" emacs) - ("https://ploum.net/atom_en.xml") - ("https://pod.fossified.com/podcast.rss" tech podcast) - ("https://pouria.dev/rss.xml") - ("https://project-mage.org/rss.xml") - ("https://protesilaos.com/master.xml" emacs philosophy) - ("https://reddit.com/r/prolog/.rss" prolog) - ("https://risky.biz/rss.xml" tech) - ("https://sachachua.com/blog/feed/" emacs) - ("https://stephanango.com/feed.xml") - ("https://stppodcast.libsyn.com/rss") - ("https://takeonrules.com/index.atom") - ("https://two-wrongs.com/feed") - ("https://typeclasses.substack.com/feed") - ("https://unixsheikh.com/feed.rss" linux) - ("https://usesthis.com/feed.atom") - ("https://writer13.neocities.org/rss.xml") - ("https://www.cs.uni.edu/~wallingf/blog/atom.xml") - ("https://www.devever.net/~hl/index.feed" tech) - ("https://www.draketo.de/rss-feed.xml") - ("https://www.evalapply.org/index.xml" emacs) - ("https://www.fsf.org/static/fsforg/rss/news.xml" tech) - ("https://www.haskellforall.com/feeds/posts/default" tech) - ("https://www.murilopereira.com/index.xml") - ("https://www.stilldrinking.org/rss/feed.xml") - ("https://www.theregister.com/headlines.atom" tech) - ("https://www.typetheoryforall.com/episodes.mp3.rss" podcast) - ("https://xeiaso.net/blog.rss" tech) - ("https://xenodium.com/rss.xml" emacs) - ("https://xkcd.com/rss.xml" comics tech) - ) - ;; don't use bold face for unread Elfeed entries - elfeed-search-face-alist '((unread (default))) - global-auto-revert-non-file-buffers t - auto-revert-verbose nil - query-about-changed-file t - kill-do-not-save-duplicates t - show-trailing-whitespace t - read-extended-command-predicate #'command-completion-default-include-p - completions-format 'one-column - completion-auto-select nil - completions-detailed nil - completion-styles '(orderless partial-completion basic) - completion-show-help nil - completions-header-format (propertize "%s candidates:\n" 'face 'shadow) - completion-auto-help 'visual - completions-max-height 16 - completion-auto-wrap t - corfu-cycle t - corfu-margin-formatters '(esy/margin-formatter) - corfu-indexed-start 1 - shell-kill-buffer-on-exit t - ;; allow disabling confirming before compilation via local variables - safe-local-variable-values '((compilation-read-command . nil)) - TeX-view-program-selection '((output-pdf "PDF Tools")) - TeX-source-correlate-start-server t - xref-show-definitions-function #'consult-xref - xref-show-xrefs-function #'consult-xref - xref-search-program 'ripgrep - sqlformat-command 'pgformatter - sql-input-ring-file-name (expand-file-name ".sqli-history" user-emacs-directory) - ;; use relative line numbers - display-line-numbers-type 'relative - ;; persist Git commit message history - savehist-additional-variables '(log-edit-comment-ring) - ) -(esy/init-step 'vars) - -;;;; load the modus-vivendi theme -(require-theme 'modus-themes) -(load-theme 'modus-vivendi) -(esy/init-step 'theme) - -;;;; set up the Iosevka font family -(set-face-attribute 'default nil :family "Iosevka" :height 130) -(set-face-attribute 'fixed-pitch nil :family "Iosevka") -(set-face-attribute 'variable-pitch nil :family "Iosevka Etoile") -(esy/init-step 'fonts) - -(add-to-list 'load-path (expand-file-name "lisp/" user-emacs-directory)) - -(setq-default indent-tabs-mode nil) - -(defvar-keymap transpose-lines-repeat-map - :doc "Repeat map for \\[transpose-lines]" - "C-t" #'transpose-lines) - -(put 'transpose-lines 'repeat-map 'transpose-lines-repeat-map) - -;;;; utility commands -(defun esy/kill-dwim () - "When region is active, kill region, otherwise kill last word." - (interactive) - (if (region-active-p) - (let ((beg (region-beginning)) - (end (region-end))) - (kill-region beg end)) - (let ((end (point))) - (backward-word) - (kill-region (point) end)))) - -(defun esy/pulse-line (&optional _) - "Pulse current line." - (interactive) - (pulse-momentary-highlight-one-line)) - -(defun esy/seconds-to-date-string (seconds) - "Decode and display the date corresponding to SECONDS." - (interactive (list (if (use-region-p) - (string-to-number (buffer-substring (region-beginning) - (region-end))) - (read-number "Unix timestamp: " (round (float-time)))))) - (message (format-time-string "%FT%T%z" (seconds-to-time seconds)))) - -(defun esy/find-init-file (init) - "Find the Emacs INIT file." - (interactive (list user-init-file)) - (find-file init)) - -(defun esy/eww-target () - (require 'eww) - (if (use-region-p) - (let ((target (buffer-substring-no-properties (use-region-beginning) - (use-region-end)))) - (add-to-history 'eww-prompt-history target) - target) - (completing-read "Browse or search: " - eww-prompt-history nil nil nil - 'eww-prompt-history - (eww-suggested-uris)))) - -(defun esy/eww (target) - "Browse or search for TARGET." - (interactive (list (esy/eww-target))) - (eww target)) - -(with-eval-after-load 'shell - (keymap-set shell-mode-map "SPC" #'comint-magic-space)) - -(defvar-local esy/log-buffer-filename nil - "File in which the current buffer is logged.") - -(defun esy/log-buffer () - "Save the current buffer under the ~/logs/ directory." - (interactive) - (let ((filename (or esy/log-buffer-filename - (setq esy/log-buffer-filename - (expand-file-name - (concat mode-name - "_" - (format-time-string - "%Y%m%d%H%M%S" - (current-time)) - ".log") - "~/logs"))))) - (save-restriction - (widen) - (write-region (point-min) - (point-max) - filename)))) - -(with-eval-after-load 'comint - (keymap-set comint-mode-map "C-c C-q" #'esy/log-buffer)) - -;;;; override the default startup message -(define-advice startup-echo-area-message (:override () report-init-time) - (format "%s started in %s. Hack away." - (propertize "Emacs" 'face 'success) - (propertize (emacs-init-time) 'face 'error ))) - -(esy/init-step 'stuff) - -;;;; set up external packages -(require 'package) - -;;;; configure non-default package archive -(add-to-list 'package-archives - '("melpa" . "http://melpa.org/packages/")) -(add-to-list 'package-archives - '("elpa-devel" . "https://elpa.gnu.org/devel/")) - -;;;; install packages -(package-install-selected-packages) - -(esy/init-step 'packages) - -;;;; enable some built-in tree-sitter modes -(add-to-list 'auto-mode-alist '("Dockerfile" . dockerfile-ts-mode)) -(add-to-list 'auto-mode-alist '("\\.ya?ml\\'" . yaml-ts-mode)) -(add-to-list 'auto-mode-alist '("\\.json\\'" . json-ts-mode)) -(add-to-list 'auto-mode-alist '("\\.rs\\'" . rust-ts-mode)) -(add-to-list 'auto-mode-alist '("\\.go\\'" . go-ts-mode)) - -;;;; enable spell checking in text buffers -(add-hook 'text-mode-hook #'flyspell-mode) - -(add-hook 'prog-mode-hook #'rainbow-delimiters-mode) -(add-hook 'prog-mode-hook #'display-line-numbers-mode) -(add-hook 'prog-mode-hook #'display-fill-column-indicator-mode) -(add-hook 'prog-mode-hook #'flyspell-prog-mode) -(add-hook 'prog-mode-hook #'flymake-mode) -(esy/init-step 'prog-mode-hooks) - -;;;; bind some keys -(keymap-global-set "C-c w" #'esy/eww) -(keymap-global-set "C-c c" #'org-capture) -(keymap-global-set "C-c l" #'org-store-link) -(keymap-global-set "C-c a" #'org-agenda) -(keymap-global-set "C-c !" #'consult-flymake) -(keymap-global-set "C-c C" #'openai-chat) -(keymap-global-set "C-c E" #'elfeed) -(keymap-global-set "C-c G" #'gnus) -(keymap-global-set "C-c M" #'mastodon) -(keymap-global-set "C-c V" #'vertalen-at-point) -(keymap-global-set "C-c F" #'esy/find-init-file) -(keymap-global-set "C-c P" #'list-packages) -(keymap-global-set "C-c SPC" #'consult-mark) -(keymap-global-set " " #'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-5-map "b" #'consult-buffer-other-frame) -(keymap-set ctl-x-map "r b" #'consult-bookmark) -(keymap-set ctl-x-map "C-b" #'ibuffer) - -(keymap-set search-map "r" #'rg) -(keymap-set search-map "l" #'rg-literal) -(keymap-set search-map "b" #'some-button) -(esy/init-step 'bindings) - -;;;; unbind some keys -(dolist (key '("s-a" "s-d" "s-e" "s-f" "s-g" "s-h" "s-j" "s-k" "s-l" - "s-m" "s-o" "s-p" "s-q" "s-s" "s-t" "s-u" "s-w" "s-x" - "s-y" "s-z")) - (keymap-global-unset key)) - -;;;; enable some commands -(dolist (command '(set-goal-column - narrow-to-region - narrow-to-page)) - (put command 'disabled nil)) - -;;;; disable some commands -(put 'suspend-frame 'disabled t) - -(with-eval-after-load 'project - (define-advice project--find-in-directory (:override (dir) no-remote-projects) - (unless (file-remote-p dir) - (run-hook-with-args-until-success 'project-find-functions dir))) - (add-to-list 'project-switch-commands '(project-compile "Compile")) - (add-to-list 'project-switch-commands '(rg-project "rg")) - (add-to-list 'project-switch-commands '(magit-project-status "Magit")) - (add-to-list 'project-switch-commands '(project-shell "Shell")) - (when (boundp 'project-prefix-map) - (define-key project-prefix-map "R" #'rg-project) - (define-key project-prefix-map "m" #'magit-project-status))) - -;;;; SQL servers -(with-eval-after-load 'sql - (setq sql-connection-alist (let* ((a (auth-source-search :port 5432 - :max 6 - :require '(:user :port :secret :host))) - (d (nth 0 a)) - (p (nth 1 a)) - (c (nth 2 a)) - (e (nth 3 a)) - (f (nth 4 a)) - (i (nth 5 a))) - `((dev - (sql-product 'postgres) - (sql-user ,(plist-get d :user)) - (sql-port 5432) - (sql-password ,(funcall (plist-get d :secret))) - (sql-server ,(plist-get d :host)) - (sql-database "alerts")) - (prod - (sql-product 'postgres) - (sql-user ,(plist-get p :user)) - (sql-port 5432) - (sql-password ,(funcall (plist-get p :secret))) - (sql-server ,(plist-get p :host)) - (sql-database "alerts")) - (cgs - (sql-product 'postgres) - (sql-user ,(plist-get c :user)) - (sql-port 5432) - (sql-password ,(funcall (plist-get c :secret))) - (sql-server ,(plist-get c :host)) - (sql-database "container_graph")) - (ten - (sql-product 'postgres) - (sql-user ,(plist-get e :user)) - (sql-port 5432) - (sql-password ,(funcall (plist-get e :secret))) - (sql-server ,(cadr (split-string (plist-get e :host) (rx "^")))) - (sql-database ,(car (split-string (plist-get e :host) (rx "^"))))) - (ac - (sql-product 'postgres) - (sql-user ,(plist-get f :user)) - (sql-port 5432) - (sql-password ,(funcall (plist-get f :secret))) - (sql-server ,(cadr (split-string (plist-get f :host) (rx "^")))) - (sql-database ,(car (split-string (plist-get f :host) (rx "^"))))) - (int - (sql-product 'postgres) - (sql-user ,(plist-get i :user)) - (sql-port 5432) - (sql-password ,(funcall (plist-get i :secret))) - (sql-server ,(cadr (split-string (plist-get i :host) (rx "^")))) - (sql-database ,(car (split-string (plist-get i :host) (rx "^")))))))) - (add-hook 'sql-interactive-mode-hook #'toggle-truncate-lines) - (define-key sql-mode-map (kbd "C-c C-f") #'sqlformat) - (define-advice sql-comint-postgres (:around (fun &rest args) use-sql-password) - (let ((process-environment - (nconc - (list (format "PGPASSWORD=%s" sql-password)) - process-environment))) - (apply fun args)))) - -;;;; Org settings -(with-eval-after-load 'org - (keymap-unset org-mode-map "C-," t)) - -(with-eval-after-load 'org-agenda - (add-to-list 'org-agenda-custom-commands - '("w" "Work TODOs" tags-todo "+work"))) - -;;;; mail settings -(with-eval-after-load 'gnus - (require 'gnus-icalendar) - (add-hook 'gnus-group-mode-hook #'gnus-topic-mode) - (gnus-icalendar-setup) - (gnus-icalendar-org-setup) - (all-the-icons-gnus-setup) - (bbdb-initialize 'gnus 'mail 'message)) - -(defun esy/local-all-the-icons-dired-mode () - (unless (file-remote-p default-directory) - (all-the-icons-dired-mode))) - -(with-eval-after-load 'dired - (put 'dired-find-alternate-file 'disabled nil) - (add-hook 'dired-mode-hook #'esy/local-all-the-icons-dired-mode)) - -;;;; workaround for Bug#62329 -;; (with-eval-after-load 'compile -;; (require 'tramp-sh)) - -(with-eval-after-load 'tramp - (tramp-set-completion-function "ssh" '((tramp-parse-netrc "~/.authinfo.gpg"))) - (add-to-list 'backup-directory-alist (cons tramp-file-name-regexp nil))) - -(with-eval-after-load 'proced - (add-hook 'proced-mode-hook #'proced-toggle-auto-update)) - -(with-eval-after-load 'time - (add-to-list 'zoneinfo-style-world-list '("Europe/Amsterdam" "Amsterdam"))) - -(with-eval-after-load 'flyspell - (keymap-unset flyspell-mode-map "C-," t) - (keymap-unset flyspell-mode-map "C-." t) - (keymap-unset flyspell-mode-map "C-;" t)) - -(add-to-list 'display-buffer-alist - '("\\*Completions\\*" - (display-buffer-reuse-window display-buffer-at-bottom) - (window-parameters . ((mode-line-format . none))))) - -(add-hook 'completion-list-mode-hook - (lambda () - (setq-local cursor-in-non-selected-windows nil))) - -(define-advice completion-file-name-table (:filter-args (args) no-remote-file-name-completion) - (let ((string (car args)) - (pred (cadr args)) - (action (caddr args))) - (if (and (file-remote-p string) (eq pred #'file-exists-p)) - (list string nil action) - (list string pred action)))) - -(define-key minibuffer-local-completion-map - [remap previous-line] #'minibuffer-previous-completion) -(define-key minibuffer-local-completion-map - [remap next-line] #'minibuffer-next-completion) - -(defun esy/dabbrev-capf () - "Workaround for issue with `dabbrev-capf'." - (require 'dabbrev) - (dabbrev--reset-global-variables) - (setq dabbrev-case-fold-search nil) - (dabbrev-capf)) - -(add-to-list 'completion-at-point-functions #'esy/dabbrev-capf) - -(defun esy/file-capf () - "File completion at point function." - (let ((bs (bounds-of-thing-at-point 'filename))) - (when bs - (let* ((start (car bs)) - (end (cdr bs))) - `(,start ,end completion--file-name-table . (:exclusive no)))))) - -(add-to-list 'completion-at-point-functions #'esy/file-capf) -(esy/init-step 'capf) - -(require 'use-package) -(esy/init-step 'use-package) -;;;; highlight the current line in line-oriented buffers -(use-package lin - :config - (add-to-list 'lin-mode-hooks 'gnus-summary-mode-hook) - (add-to-list 'lin-mode-hooks 'gnus-group-mode-hook) - (add-to-list 'lin-mode-hooks 'gnus-server-mode-hook)) -(esy/init-step 'lin) - -(use-package all-the-icons - :config - (add-to-list 'all-the-icons-extension-icon-alist - '("pl" all-the-icons-alltheicon "prolog" - :height 1.1 :face all-the-icons-lmaroon))) -(esy/init-step 'icons) - -(defun esy/margin-formatter (metadata) - "Format METADATA for `corfu-margin-formatters'." - (pcase (cdr (assoc 'category metadata)) - ('file (lambda (string) - (concat (if (string-suffix-p "/" string) - (all-the-icons-icon-for-dir string) - (all-the-icons-icon-for-file string)) - " "))) - ('dabbrev (lambda (_) "… ")))) - -(use-package marginalia - :config - (add-hook 'marginalia-mode-hook #'all-the-icons-completion-marginalia-setup)) -(esy/init-step 'marginalia) - -;; enable some global minor modes -(dolist-with-progress-reporter (mode '( - column-number-mode - context-menu-mode - corfu-indexed-mode - display-time-mode - display-battery-mode - global-auto-revert-mode - global-corfu-mode - global-diff-hl-mode - global-whitespace-cleanup-mode - lin-global-mode - marginalia-mode - minibuffer-depth-indicate-mode - mouse-avoidance-mode - pixel-scroll-precision-mode - recentf-mode - repeat-mode - save-place-mode - savehist-mode - show-paren-mode - transient-mark-mode - )) - "Enabling minor modes" - (funcall mode)) -(esy/init-step 'minor-modes) - -(emms-minimalistic) -(esy/init-step 'emms) - -;;;; PDF settings -(use-package pdf-tools - :mode ("\\.pdf\\'" . pdf-view-mode) - :config - (pdf-tools-install :no-query) - (add-hook 'pdf-view-mode-hook #'pdf-view-midnight-minor-mode) - (add-to-list 'revert-without-query "\\.pdf\\'")) -(esy/init-step 'pdf) - -(with-eval-after-load 'tex - (add-hook 'TeX-after-compilation-finished-functions - #'TeX-revert-document-buffer)) -(esy/init-step 'tex) - -(with-eval-after-load 'vterm - (add-to-list 'vterm-tramp-shells '("kubernetes" "/bin/bash"))) -;; (use-package vterm -;; :config -;; (add-to-list 'vterm-tramp-shells '("kubernetes" "/bin/bash"))) -(esy/init-step 'vterm) - -(use-package paredit - :hook lisp-data-mode - :config - (keymap-unset paredit-mode-map "M-s" t) - (keymap-unset paredit-mode-map "M-?" t)) -(esy/init-step 'paredit) - -(use-package haskell-mode - :hook ((haskell-mode . interactive-haskell-mode) - (haskell-mode . haskell-decl-scan-mode) - (haskell-mode . haskell-doc-mode))) -(esy/init-step 'haskell) - -(use-package sweeprolog - :mode ("\\.plt?\\'" . sweeprolog-mode) - :bind-keymap ("C-c p" . sweeprolog-prefix-map) - :config - (add-hook 'sweeprolog-mode-hook #'sweeprolog-electric-layout-mode) - (add-hook 'sweeprolog-top-level-mode-hook #'compilation-shell-minor-mode) + :prepend t + :empty-lines 1 + :immediate-finish t)) + ;; point Org to file agenda file + org-agenda-files '("~/org/inbox.org") + org-default-notes-file "~/org/inbox.org" + ;; weeks start on Sunday + org-agenda-start-on-weekday 0 + ;; use a nice unicode ellipsis + org-ellipsis "…" + ;; open everything withing Emacs + org-file-apps '((t . emacs)) + ;; enable Org speed commands + org-use-speed-commands t + ;; task states + org-todo-keywords '((sequence + "TODO(t)" + "BLOCKED(b@/!)" + "INPROGRESS(i!)" + "|" + "DONE(d!)" + "CANCELED(c@)")) + ;; set a task to INPROGRESS when I clock into it + org-clock-in-switch-to-state "INPROGRESS" + ;; log the finish time of tasks in Org + org-log-done 'time + org-log-into-drawer t + ;; minimal prompt for task states + org-use-fast-todo-selection 'expert + ;; set up a group of mutual exclusive tags + org-tag-alist '((:startgroup) + ("work" . ?w) + ("studies" . ?s) + ("esols" . ?e) + ("personal" . ?p) + (:endgroup)) + ;; minimal prompt for tags + org-fast-tag-selection-single-key 'expert + ;; allow evaluating some languages with Org Babel + org-babel-load-languages '((emacs-lisp . t) + (shell . t) + (sql . t) + (prolog . t)) + ;; do not ask for confirmation when evaluating Org source blocks + org-confirm-babel-evaluate nil + ;; extra modules to load along with Org + org-modules '(ol-bbdb + ol-bibtex + ol-docview + ol-gnus + ol-info + ol-irc + ol-mhe + ol-rmail + ol-eww + ob-sql + org-tempo) + ;; refile Org entries to my agenda file(s) + org-refile-targets '((org-agenda-files . (:maxlevel . 5)) + (nil . (:maxlevel . 3))) + ;; use full outline path when prompting for refile target + org-refile-use-outline-path t + ;; archive for Org entries + org-archive-location "~/org/journal.org::datetree/* Finished Tasks :ARCHIVE:" + ;; enable italic text in code + modus-themes-italic-constructs t + ;; enable bold text in code + modus-themes-bold-constructs t + ;; use fixed-pitch text where appropriate + modus-themes-mixed-fonts t + ;; make prompts bold + modus-themes-prompts '(bold) + ;; give source blocks a distinct background shade + modus-themes-org-blocks 'gray-background + ;; use variable-pitch text here are there + modus-themes-variable-pitch-ui t + ;; fine-tune some theme settings + modus-themes-common-palette-overrides '((border-mode-line-active unspecified) + (border-mode-line-inactive unspecified) + (bg-mode-line-active bg-blue-intense) + (fg-mode-line-active fg-main) + (fringe unspecified) + (underline-link border) + (underline-link-visited border) + (underline-link-symbolic border) + (fg-region unspecified)) + ;; follow links to version-controlled files without confirming + vc-follow-symlinks t + ;; jump from .c to .h files with find-sibling-file + find-sibling-rules '(("\\([^/]+\\)\\.c\\'" "\\1.h")) + ;; configure multiple SMTP accounts + smtpmail-multi-accounts '((esy . ("eshelshay.yaron@gmail.com" + "smtp.gmail.com" 587 + "eshelshay.yaron@gmail.com" + starttls nil nil nil)) + (swp . ("eshel@swi-prolog.org" + "mail.swi-prolog.com" 587 + "eshel@swi-prolog.org" + starttls nil nil nil)) + (me . ("me@eshelyaron.com" + "mail.eshelyaron.com" + 587 + "me@eshelyaron.com" + starttls nil nil nil))) + ;; associate email addresses with SMTP accounts + smtpmail-multi-associations '(("[^z-a]*eshelshay\\.yaron@gmail\\.com[^z-a]*" esy) + ("[^z-a]*eshel@swi-prolog\\.org[^z-a]*" swp) + ("[^z-a]*me@eshelyaron\\.com[^z-a]*" me)) + ;; set function for sending email + send-mail-function #'smtpmail-multi-send-it + message-send-mail-function #'smtpmail-multi-send-it + ;; set email user agent + mail-user-agent 'gnus-user-agent + ;; make Gnus not prompt as much + gnus-expert-user t + ;; make Gnus read the dribble file on startup without prompting + gnus-always-read-dribble-file t + ;; don't break pages in incoming email + gnus-break-pages nil + ;; don't show the Gnus startup screen + gnus-inhibit-startup-message t + ;; set Gnus backends + gnus-select-method '(nntp "news.gmane.io") + gnus-secondary-select-methods '((nnimap "gmail" + (nnimap-address "imap.gmail.com") + (nnimap-server-port "imaps") + (nnimap-stream ssl)) + (nnimap "me" + (nnimap-address "mail.eshelyaron.com") + (nnimap-server-port "imaps") + (nnimap-stream ssl)) + (nnimap "swi" + (nnimap-address "mail.swi-prolog.com") + (nnimap-server-port "imaps") + (nnimap-stream ssl))) + ;; simpler message + gnus-no-groups-message "No new articles" + ;; don't close other windows when opening Gnus + gnus-use-full-window nil + ;; make Gnus handle this content types + gnus-article-treat-types '("text/plain" + "text/x-verbatim" + "text/x-patch" + "text/html" + "text/calendar") + ;; capture calendar events from Gnus to my Org inbox file + gnus-icalendar-org-capture-file "~/org/inbox.org" + ;; put calendar events under the Calendar heading + gnus-icalendar-org-capture-headline '("Calendar") + ;; use ISO format for calendar dates + calendar-date-style 'iso + ;; maintain legibility of rendered text in HTML mails + shr-color-visible-luminance-min 75 + ;; my Mastodon settings + mastodon-instance-url "https://emacs.ch" + mastodon-active-user "eshel" + ;; direct Magit to my Git checkouts directory + magit-repository-directories '(("~/checkouts/" . 1)) + ;; have Dired operations target another visible Dired buffer + dired-dwim-target t + ;; use zsh in vterm by default + vterm-shell "/bin/zsh" + vterm-max-scrollback 2048 + vterm-kill-buffer-on-exit nil + vterm-use-vterm-prompt-detection-method t + ;; use MPV with EMMS + emms-player-list '(emms-player-mpv) + bbdb-phone-style nil + eww-auto-rename-buffer 'title + browse-url-browser-function #'eww-browse-url + browse-url-generic-program "open" + ;; set up some feeds for Elfeed + elfeed-feeds '( + ("https://snarfed.org/feed" tech) + ("https://www.nirandfar.com/rss" tech blog) + ("https://www.joelotter.com/posts/index.xml" indieweb) + ("https://dri.es/rss.xml" indieweb) + ("https://www.thisdaysportion.com/feed/index.xml" indieweb) + ("http://regex.info/blog/feed" tech blog japan) + ("https://eshelyaron.com/rss.xml" me) + ("https://www.brautaset.org/feed.xml" blog tech) + ("http://node2.feed43.com/7487052648530856.xml" comics scifi) + ("https://ajroach42.com/feed.xml") + ("https://writepermission.com/rss.xml" tech blog) + ("https://alexschroeder.ch/wiki/feed/full/" tech) + ("https://amodernist.com/all.atom" emacs) + ("https://archive.casouri.cc/note/atom.xml") + ("https://arcology.garden/updates.xml") + ("http://www.aaronsw.com/weblog/index.xml" blog tech) + ("https://atthis.link/rss.xml" tech) + ("https://daily.baty.net/feed/feed.xml" blog tech) + ("https://baty.net/feed" tech) + ("https://betterappsec.com/feed") + ("https://bitspook.in/blog/feed.xml") + ("https://blog.acthompson.net/feeds/posts/default") + ("https://bruda.ca/feed.php") + ("https://cce.whatthefuck.computer/updates.xml") + ("https://cdn.jwz.org/blog/feed/" tech) + ("https://cestlaz.github.io/rss.xml" tech) + ("https://changelog.complete.org/feed") + ("https://daniel.haxx.se/blog/feed/" tech) + ("https://drewdevault.com/blog/index.xml") + ("https://emacs.dyerdwelling.family/index.xml") + ("https://emacsninja.com/feed.atom" emacs) + ("https://erikmcclure.com/blog/index.xml") + ("https://evanhahn.com/blog/index.xml") + ("https://fasterthanli.me/index.xml") + ("https://feeds.buzzsprout.com/2134279.rss") + ("https://flower.codes/feed.xml") + ("https://garymarcus.substack.com/feed") + ("https://gwern.substack.com/feed" tech blog) + ("https://herman.bearblog.dev/feed/") + ("https://irreal.org/blog/?feed=rss2" emacs) + ("https://jcm.libsyn.com/rss/") + ("https://jnd.org/feed" tech philosophy) + ("https://kelar.org/~bandali/atom.xml" tech blog) + ("https://kitchen-sink.kwakk.info/feed" comics) + ("https://lars.ingebrigtsen.no/feed" emacs) + ("https://lwn.net/headlines/rss" linux) + ("https://maggieappleton.com/rss.xml" design) + ("https://matklad.github.io/feed.xml") + ("https://matt-rickard.com/rss" tech) + ("https://njoseph.me/shaarli/feed/atom?") + ("https://nullprogram.com/feed/" tech) + ("https://olddeuteronomy.github.io/index.xml") + ("https://parasurv.neocities.org/rss.xml" tech blog) + ("https://phaazon.net/blog/feed") + ("https://planet.emacslife.com/atom.xml" emacs) + ("https://ploum.net/atom_en.xml") + ("https://pod.fossified.com/podcast.rss" tech podcast) + ("https://pouria.dev/rss.xml") + ("https://project-mage.org/rss.xml") + ("https://protesilaos.com/master.xml" emacs philosophy) + ("https://reddit.com/r/prolog/.rss" prolog) + ("https://risky.biz/rss.xml" tech) + ("https://sachachua.com/blog/feed/" emacs) + ("https://stephanango.com/feed.xml") + ("https://stppodcast.libsyn.com/rss") + ("https://takeonrules.com/index.atom") + ("https://two-wrongs.com/feed") + ("https://typeclasses.substack.com/feed") + ("https://unixsheikh.com/feed.rss" linux) + ("https://blog.gregbrockman.com/feed" tech ai) + ("https://usesthis.com/feed.atom") + ("https://writer13.neocities.org/rss.xml") + ("https://www.cs.uni.edu/~wallingf/blog/atom.xml") + ("https://www.devever.net/~hl/index.feed" tech) + ("https://www.draketo.de/rss-feed.xml") + ("https://www.evalapply.org/index.xml" emacs) + ("https://www.fsf.org/static/fsforg/rss/news.xml" tech) + ("https://www.haskellforall.com/feeds/posts/default" tech) + ("https://www.murilopereira.com/index.xml") + ("https://www.stilldrinking.org/rss/feed.xml") + ("https://www.theregister.com/headlines.atom" tech) + ("https://www.typetheoryforall.com/episodes.mp3.rss" podcast) + ("https://xeiaso.net/blog.rss" tech) + ("https://xenodium.com/rss.xml" emacs) + ("https://xkcd.com/rss.xml" comics tech) + ("http://sawv.org/rss.xml" tech blog) + ) + ;; don't use bold face for unread Elfeed entries + elfeed-search-face-alist '((unread (default))) + global-auto-revert-non-file-buffers t + auto-revert-verbose nil + query-about-changed-file t + kill-do-not-save-duplicates t + show-trailing-whitespace t + read-extended-command-predicate #'command-completion-default-include-p + completions-format 'one-column + completion-auto-select nil + completions-detailed nil + completion-styles '(orderless partial-completion basic) + completion-show-help nil + completions-header-format (propertize "%s candidates:\n" 'face 'shadow) + completion-auto-help 'visual + completions-max-height 16 + completion-auto-wrap t + corfu-cycle t + corfu-indexed-start 1 + shell-kill-buffer-on-exit t + ;; allow disabling confirming before compilation via local variables + safe-local-variable-values '((compilation-read-command . nil)) + TeX-view-program-selection '((output-pdf "PDF Tools")) + TeX-source-correlate-start-server t + xref-show-definitions-function #'consult-xref + xref-show-xrefs-function #'consult-xref + xref-search-program 'ripgrep + sqlformat-command 'pgformatter + sql-input-ring-file-name (expand-file-name ".sqli-history" user-emacs-directory) + ;; use relative line numbers + display-line-numbers-type 'relative + ;; persist Git commit message history + savehist-additional-variables '(log-edit-comment-ring) + ) + (setq-default indent-tabs-mode nil)) + +(esy/init-step theme + "Load the `modus-vivendi' theme." + (require-theme 'modus-themes) + (load-theme 'modus-vivendi)) + +(esy/init-step fonts + "Set up the Iosevka font family." + (set-face-attribute 'default nil :family "Iosevka" :height 130) + (set-face-attribute 'fixed-pitch nil :family "Iosevka") + (set-face-attribute 'variable-pitch nil :family "Iosevka Etoile")) + +(esy/init-step load-path + "Add custom code directory to `load-path'." + (add-to-list 'load-path (expand-file-name "lisp/" user-emacs-directory)) + (autoload 'some-button "some-button" nil t) + (autoload 'esy-capture "esy-capture" nil t) + (autoload 'pdf-view-mode "pdf-view" nil t)) + +(esy/init-step commands + "Define custom commands." + (defvar-keymap transpose-lines-repeat-map + :doc "Repeat map for \\[transpose-lines]" + "C-t" #'transpose-lines) + + (put 'transpose-lines 'repeat-map 'transpose-lines-repeat-map) + + ;; utility commands + (defun esy/kill-dwim () + "When region is active, kill region, otherwise kill last word." + (interactive) + (if (region-active-p) + (let ((beg (region-beginning)) + (end (region-end))) + (kill-region beg end)) + (let ((end (point))) + (backward-word) + (kill-region (point) end)))) + + (defun esy/pulse-line (&optional _) + "Pulse current line." + (interactive) + (pulse-momentary-highlight-one-line)) + + (defun esy/seconds-to-date-string (seconds) + "Decode and display the date corresponding to SECONDS." + (interactive (list (if (use-region-p) + (string-to-number (buffer-substring (region-beginning) + (region-end))) + (read-number "Unix timestamp: " (round (float-time)))))) + (message (format-time-string "%FT%T%z" (seconds-to-time seconds)))) + + (defun esy/find-init-file (init) + "Find the Emacs INIT file." + (interactive (list user-init-file)) + (find-file init)) + + (defun esy/eww-target () + (require 'eww) + (if (use-region-p) + (let ((target (buffer-substring-no-properties (use-region-beginning) + (use-region-end)))) + (add-to-history 'eww-prompt-history target) + target) + (completing-read "Browse or search: " + eww-prompt-history nil nil nil + 'eww-prompt-history + (eww-suggested-uris)))) + + (defun esy/eww (target) + "Browse or search for TARGET." + (interactive (list (esy/eww-target))) + (eww target)) + + (with-eval-after-load 'shell + (keymap-set shell-mode-map "SPC" #'comint-magic-space)) + + (defvar-local esy/log-buffer-filename nil + "File in which the current buffer is logged.") + + (defun esy/log-buffer () + "Save the current buffer under the ~/logs/ directory." + (interactive) + (let ((filename (or esy/log-buffer-filename + (setq esy/log-buffer-filename + (expand-file-name + (concat mode-name + "_" + (format-time-string + "%Y%m%d%H%M%S" + (current-time)) + ".log") + "~/logs"))))) + (save-restriction + (widen) + (write-region (point-min) + (point-max) + filename)))) + + (with-eval-after-load 'comint + (keymap-set comint-mode-map "C-c C-q" #'esy/log-buffer))) + +(esy/init-step startup-message + "Override the default startup message." + (define-advice startup-echo-area-message (:override () report-init-time) + (format "%s started in %s. Hack away." + (propertize "Emacs" 'face 'success) + (propertize (emacs-init-time) 'face 'error )))) + +(esy/init-step packages + "Ensure external packages are installed." + (package-install-selected-packages)) + +(esy/init-step prog-mode-hook + "Extend standard programming mode hooks." + (dolist (mode '(bug-reference-prog-mode + display-fill-column-indicator-mode + display-line-numbers-mode + flymake-mode + flyspell-prog-mode + rainbow-delimiters-mode)) + (add-hook 'prog-mode-hook mode)) + (add-hook 'lisp-data-mode-hook #'paredit-mode)) + +(esy/init-step other-hooks + "Extend other standard hooks." + (add-hook 'text-mode-hook #'flyspell-mode) + (add-hook 'completion-list-mode-hook + (lambda () + (setq-local cursor-in-non-selected-windows nil)))) + +(esy/init-step bindings + "Bind some keys." + (keymap-global-set "C-c w" #'esy/eww) + (keymap-global-set "C-c c" #'org-capture) + (keymap-global-set "C-c l" #'org-store-link) + (keymap-global-set "C-c a" #'org-agenda) + (keymap-global-set "C-c v" #'vterm-other-window) + (keymap-global-set "C-c S" #'scratch-buffer) + (keymap-global-set "C-c m" #'esy/emms-map) + (keymap-global-set "C-c !" #'consult-flymake) + (keymap-global-set "C-c C" #'esy-capture) + (keymap-global-set "C-c O" #'openai-chat) + (keymap-global-set "C-c E" #'elfeed) + (keymap-global-set "C-c G" #'gnus) + (keymap-global-set "C-c M" #'mastodon) + (keymap-global-set "C-c V" #'vertalen-at-point) + (keymap-global-set "C-c F" #'esy/find-init-file) + (keymap-global-set "C-c P" #'list-packages) + (keymap-global-set "C-c SPC" #'consult-mark) + (keymap-global-set " " #'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-5-map "b" #'consult-buffer-other-frame) + (keymap-set ctl-x-map "r b" #'consult-bookmark) + (keymap-set ctl-x-map "C-b" #'ibuffer) + + (keymap-set search-map "r" #'rg) + (keymap-set search-map "l" #'rg-literal) + (keymap-set search-map "b" #'some-button) + + ;; unbind some keys + (dolist (key '("s-a" "s-d" "s-e" "s-f" "s-g" "s-h" "s-j" "s-k" "s-l" + "s-m" "s-o" "s-p" "s-q" "s-s" "s-t" "s-u" "s-w" "s-x" + "s-y" "s-z")) + (keymap-global-unset key)) + + ;; enable some commands + (dolist (command '(set-goal-column + narrow-to-region + narrow-to-page + downcase-region + upcase-region)) + (put command 'disabled nil)) + + ;; disable some commands + (put 'suspend-frame 'disabled t)) + +(esy/init-step project + "Configure project management commands." + (with-eval-after-load 'project + (define-advice project--find-in-directory (:override (dir) no-remote-projects) + (unless (file-remote-p dir) + (run-hook-with-args-until-success 'project-find-functions dir))) + (add-to-list 'project-switch-commands '(project-compile "Compile")) + (add-to-list 'project-switch-commands '(rg-project "rg")) + (add-to-list 'project-switch-commands '(magit-project-status "Magit")) + (add-to-list 'project-switch-commands '(project-shell "Shell")) + (when (boundp 'project-prefix-map) + (define-key project-prefix-map "R" #'rg-project) + (define-key project-prefix-map "m" #'magit-project-status)))) + +(esy/init-step sql + "Configure SQL connections." + (with-eval-after-load 'sql + (setq sql-connection-alist (let* ((a (auth-source-search :port 5432 + :max 6 + :require '(:user :port :secret :host))) + (d (nth 0 a)) + (p (nth 1 a)) + (c (nth 2 a)) + (e (nth 3 a)) + (f (nth 4 a)) + (i (nth 5 a))) + `((dev + (sql-product 'postgres) + (sql-user ,(plist-get d :user)) + (sql-port 5432) + (sql-password ,(funcall (plist-get d :secret))) + (sql-server ,(plist-get d :host)) + (sql-database "alerts")) + (prod + (sql-product 'postgres) + (sql-user ,(plist-get p :user)) + (sql-port 5432) + (sql-password ,(funcall (plist-get p :secret))) + (sql-server ,(plist-get p :host)) + (sql-database "alerts")) + (cgs + (sql-product 'postgres) + (sql-user ,(plist-get c :user)) + (sql-port 5432) + (sql-password ,(funcall (plist-get c :secret))) + (sql-server ,(plist-get c :host)) + (sql-database "container_graph")) + (ten + (sql-product 'postgres) + (sql-user ,(plist-get e :user)) + (sql-port 5432) + (sql-password ,(funcall (plist-get e :secret))) + (sql-server ,(cadr (split-string (plist-get e :host) (rx "^")))) + (sql-database ,(car (split-string (plist-get e :host) (rx "^"))))) + (ac + (sql-product 'postgres) + (sql-user ,(plist-get f :user)) + (sql-port 5432) + (sql-password ,(funcall (plist-get f :secret))) + (sql-server ,(cadr (split-string (plist-get f :host) (rx "^")))) + (sql-database ,(car (split-string (plist-get f :host) (rx "^"))))) + (int + (sql-product 'postgres) + (sql-user ,(plist-get i :user)) + (sql-port 5432) + (sql-password ,(funcall (plist-get i :secret))) + (sql-server ,(cadr (split-string (plist-get i :host) (rx "^")))) + (sql-database ,(car (split-string (plist-get i :host) (rx "^")))))))) + (add-hook 'sql-interactive-mode-hook #'toggle-truncate-lines) + (define-key sql-mode-map (kbd "C-c C-f") #'sqlformat) + (define-advice sql-comint-postgres (:around (fun &rest args) use-sql-password) + (let ((process-environment + (nconc + (list (format "PGPASSWORD=%s" sql-password)) + process-environment))) + (apply fun args))))) + +(esy/init-step org + "Configure Org mode." (with-eval-after-load 'org - (add-to-list 'org-src-lang-modes '("prolog" . sweeprolog)))) -(esy/init-step 'sweep) - -(with-eval-after-load 'rg - (add-to-list 'rg-custom-type-aliases '("Prolog" . "*.pl *.plt *.pro *.prolog"))) -;; (use-package rg -;; :config -;; (add-to-list 'rg-custom-type-aliases '("Prolog" . "*.pl *.plt *.pro *.prolog"))) -(esy/init-step 'rg) - -(use-package terraform-mode - :config - (setq terraform--resource-name-face font-lock-function-name-face - terraform--resource-type-face font-lock-type-face)) -(esy/init-step 'terraform) - -(with-eval-after-load 'consult - (with-eval-after-load 'embark - (require 'embark-consult))) -(esy/init-step 'embark-consult) - -;;;; compile this file (if changed) when Emacs is killed -(defun esy/compile-config () - (when (file-newer-than-file-p user-init-file - (byte-compile-dest-file user-init-file)) - (byte-compile-file user-init-file))) - -(add-hook 'kill-emacs-hook #'esy/compile-config 10) -(esy/init-step 'kill-hook) - -(let ((last nil)) - (dolist (step (reverse esy/init-step-times)) - (message "%s (%f) %s" - (format-time-string "%T.%N" (cdr step)) - (if last - (float-time (time-subtract (cdr step) last)) - 0) - (car step)) - (setq last (cdr step)))) + (keymap-unset org-mode-map "C-," t) + (add-to-list 'org-src-lang-modes '("prolog" . sweeprolog))) + + (with-eval-after-load 'org-agenda + (add-to-list 'org-agenda-custom-commands + '("w" "Work TODOs" tags-todo "+work")))) + +(esy/init-step email + "Configure email via `gnus'." + (with-eval-after-load 'gnus + (require 'gnus-icalendar) + (add-hook 'gnus-group-mode-hook #'gnus-topic-mode) + (gnus-icalendar-setup) + (gnus-icalendar-org-setup) + (all-the-icons-gnus-setup) + (bbdb-initialize 'gnus 'mail 'message))) + +(esy/init-step dired + "Configure `dired'." + (defun esy/local-all-the-icons-dired-mode () + (unless (file-remote-p default-directory) + (all-the-icons-dired-mode))) + + (with-eval-after-load 'dired + (put 'dired-find-alternate-file 'disabled nil) + (add-hook 'dired-mode-hook #'esy/local-all-the-icons-dired-mode))) + +(esy/init-step tramp + "Configure remote access via `tramp'." + (with-eval-after-load 'tramp + (tramp-set-completion-function "ssh" '((tramp-parse-netrc "~/.authinfo.gpg"))) + (add-to-list 'backup-directory-alist (cons tramp-file-name-regexp nil)))) + +(esy/init-step proced + "Configure `proced'." + (with-eval-after-load 'proced + (add-hook 'proced-mode-hook #'proced-toggle-auto-update))) + +(esy/init-step time + "Configure `world-clock'." + (with-eval-after-load 'time + (add-to-list 'zoneinfo-style-world-list '("Europe/Amsterdam" "Amsterdam")))) + +(esy/init-step flyspell + "Configure spelling errors correction via `flyspell'." + (with-eval-after-load 'flyspell + (keymap-unset flyspell-mode-map "C-," t) + (keymap-unset flyspell-mode-map "C-." t) + (keymap-unset flyspell-mode-map "C-;" t))) + +(esy/init-step minibuffer-completion + "Configure minibuffer completions." + (add-to-list 'display-buffer-alist + '("\\*Completions\\*" + (display-buffer-reuse-window display-buffer-at-bottom) + (window-parameters . ((mode-line-format . none))))) + + (define-advice completion-file-name-table (:filter-args (args) no-remote-file-name-completion) + (let ((string (car args)) + (pred (cadr args)) + (action (caddr args))) + (if (and (file-remote-p string) (eq pred #'file-exists-p)) + (list string nil action) + (list string pred action)))) + + (define-key minibuffer-local-completion-map + [remap previous-line] #'minibuffer-previous-completion) + (define-key minibuffer-local-completion-map + [remap next-line] #'minibuffer-next-completion) + + (with-eval-after-load 'marginalia + (add-hook 'marginalia-mode-hook #'all-the-icons-completion-marginalia-setup)) + + (with-eval-after-load 'consult + (with-eval-after-load 'embark + (require 'embark-consult)))) + +(esy/init-step capf + "Set up some global `completion-at-point-functions'." + (defun esy/dabbrev-capf () + "Workaround for issue with `dabbrev-capf'." + (require 'dabbrev) + (dabbrev--reset-global-variables) + (setq dabbrev-case-fold-search nil) + (dabbrev-capf)) + + (add-to-list 'completion-at-point-functions #'esy/dabbrev-capf) + + (defun esy/file-capf () + "File completion at point function." + (let ((bs (bounds-of-thing-at-point 'filename))) + (when bs + (let* ((start (car bs)) + (end (cdr bs))) + `(,start ,end completion--file-name-table . (:exclusive no)))))) + + (add-to-list 'completion-at-point-functions #'esy/file-capf)) + +(esy/init-step lin + "Configure highlighting of the current line via `lin'." + (with-eval-after-load 'lin + (add-to-list 'lin-mode-hooks 'gnus-summary-mode-hook) + (add-to-list 'lin-mode-hooks 'gnus-group-mode-hook) + (add-to-list 'lin-mode-hooks 'gnus-server-mode-hook))) + +(esy/init-step icons + "Configure icons via `all-the-icons'." + (with-eval-after-load 'all-the-icons + (add-to-list 'all-the-icons-extension-icon-alist + '("pl" all-the-icons-alltheicon "prolog" + :height 1.1 :face all-the-icons-lmaroon)))) + +(esy/init-step corfu + "Configure `completion-in-region' UI with `corfu'." + (let ((original-completion-in-region-function completion-in-region-function)) + (defun esy/setup-corfu (&rest args) + (setq completion-in-region-function original-completion-in-region-function) + (global-corfu-mode) + (apply #'completion-in-region args))) + (setq completion-in-region-function #'esy/setup-corfu) + (with-eval-after-load 'corfu + (defun esy/margin-formatter (metadata) + "Format METADATA for `corfu-margin-formatters'." + (pcase (cdr (assoc 'category metadata)) + ('file (lambda (string) + (concat (if (string-suffix-p "/" string) + (all-the-icons-icon-for-dir string) + (all-the-icons-icon-for-file string)) + " "))) + ('dabbrev (lambda (_) "… ")))) + (add-to-list 'corfu-margin-formatters #'esy/margin-formatter) + (corfu-indexed-mode))) + +(esy/init-step minor-modes + "Enable some global minor modes." + (dolist (mode '( + column-number-mode + context-menu-mode + display-time-mode + display-battery-mode + global-auto-revert-mode + global-diff-hl-mode + global-whitespace-cleanup-mode + lin-global-mode + marginalia-mode + minibuffer-depth-indicate-mode + mouse-avoidance-mode + pixel-scroll-precision-mode + recentf-mode + repeat-mode + save-place-mode + savehist-mode + show-paren-mode + transient-mark-mode + )) + (funcall mode))) + +(esy/init-step emms + "Set up EMMS." + (dolist (command '(emms-start + emms-stop + emms-next + emms-previous + emms-pause + emms-seek + emms-seek-to + emms-seek-forward + emms-seek-backward + emms-show)) + (autoload command "emms")) + + (defvar-keymap esy/emms-map + :doc "My keymap for EMMS commands." + :prefix 'esy/emms-map + "N" #'emms-next + "P" #'emms-previous + "S" #'emms-stop + "b" #'emms-seek-backward + "f" #'emms-seek-forward + "k" #'emms-seek + "m" #'emms-show + "p" #'emms-pause + "s" #'emms-start + "t" #'emms-seek-to) + + (with-eval-after-load 'emms + (emms-minimalistic))) + +(esy/init-step pdf + "Configure PDF handling." + (with-eval-after-load 'pdf-view + (require 'pdf-tools)) + (with-eval-after-load 'pdf-tools + (pdf-tools-install :no-query) + (add-hook 'pdf-view-mode-hook #'pdf-view-midnight-minor-mode)) + (add-to-list 'revert-without-query "\\.pdf\\'")) + +(esy/init-step tex + "Configure TeX via `auctex'." + (with-eval-after-load 'tex + (add-hook 'TeX-after-compilation-finished-functions + #'TeX-revert-document-buffer))) + +(esy/init-step vterm + "Configure terminal emulation via `vterm'." + (with-eval-after-load 'vterm + (add-to-list 'vterm-tramp-shells '("kubernetes" "/bin/bash")))) + +(esy/init-step paredit + "Remove some over-intrusive keybindings in `paredit'." + (with-eval-after-load 'paredit + (keymap-unset paredit-mode-map "M-s" t) + (keymap-unset paredit-mode-map "M-?" t))) + +(esy/init-step sweep + "Configure Prolog integration via `sweeprolog'." + (with-eval-after-load 'sweeprolog + (add-hook 'sweeprolog-mode-hook #'sweeprolog-electric-layout-mode) + (add-hook 'sweeprolog-top-level-mode-hook #'compilation-shell-minor-mode) + (keymap-global-set "C-c p" sweeprolog-prefix-map))) + +(esy/init-step rg + "Configure recursive grepping via `rg'." + (with-eval-after-load 'rg + (add-to-list 'rg-custom-type-aliases '("Prolog" . "*.pl *.plt *.pro *.prolog")))) + +(esy/init-step terraform + "Configure custom faces in `terraform-mode'." + (with-eval-after-load 'terraform-mode + (setq terraform--resource-name-face font-lock-function-name-face + terraform--resource-type-face font-lock-type-face))) + +(esy/init-step auto-mode + "Associate major modes with files based on their extensions." + (dolist (cell '(("Dockerfile" . dockerfile-ts-mode) + ("\\.ya?ml\\'" . yaml-ts-mode) + ("\\.json\\'" . json-ts-mode) + ("\\.rb\\'" . ruby-ts-mode) + ("\\.rs\\'" . rust-ts-mode) + ("\\.go\\'" . go-ts-mode) + ("\\.pdf\\'" . pdf-view-mode) + ("\\.plt?\\'" . sweeprolog-mode))) + (push cell auto-mode-alist))) + +(esy/init-step kill-hook + "Compile this file (if changed) when Emacs is killed." + (defun esy/compile-config () + (interactive) + (when (file-newer-than-file-p user-init-file + (byte-compile-dest-file user-init-file)) + (byte-compile-file user-init-file)) + (when (file-newer-than-file-p early-init-file + (byte-compile-dest-file early-init-file)) + (byte-compile-file early-init-file))) + + (add-hook 'kill-emacs-hook #'esy/compile-config 10)) + +(dolist (step (sort esy/init-step-times (lambda (l r) (time-less-p (cdr r) (cdr l))))) + (message "%f %s" + (float-time (cdr step)) + (car step))) (provide 'init) ;;; init.el ends here diff --git a/.emacs.d/lisp/esy-capture.el b/.emacs.d/lisp/esy-capture.el new file mode 100644 index 0000000..3fdb739 --- /dev/null +++ b/.emacs.d/lisp/esy-capture.el @@ -0,0 +1,51 @@ +;;; esy-capture.el --- capture a new post -*- lexical-binding: t; -*- + +;; Copyright (C) 2023 Eshel Yaron + +;; Author: Eshel Yaron +;; Keywords: convenience + +;; This program is free software; you can redistribute it and/or modify +;; it under the terms of the GNU General Public License as published by +;; the Free Software Foundation, either version 3 of the License, or +;; (at your option) any later version. + +;; This program is distributed in the hope that it will be useful, +;; but WITHOUT ANY WARRANTY; without even the implied warranty of +;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +;; GNU General Public License for more details. + +;; You should have received a copy of the GNU General Public License +;; along with this program. If not, see . + +;;; Commentary: + +;; + +;;; Code: + +(defvar esy-capture-directory "~/checkouts/eshelyaron.com/org/posts") + +(defvar esy-capture-keywords '("emacs" "prolog" "language" "politics")) + +(defun esy-capture (title subtitle description keywords date) + (interactive (list (read-string "Title: ") + (read-string "Subtitle: ") + (read-string "Description: ") + (completing-read-multiple "Keywords: " esy-capture-keywords) + (format-time-string "%F"))) + (find-file (expand-file-name + (concat date + "-" + (downcase (string-join (string-split title (rx (+ (not alnum))) t) "-")) + ".org") + esy-capture-directory)) + (insert "#+TITLE: " title "\n" + "#+SUBTITLE: " subtitle "\n" + "#+DESCRIPTION: " description "\n" + "#+KEYWORDS: " (string-join keywords " ") "\n" + "#+DATE: " date "\n") + (set-buffer-modified-p nil)) + +(provide 'esy-capture) +;;; esy-capture.el ends here -- 2.39.2