From 9eaa89992fc476e5a01ebb5340de6bb0ddf0aaab Mon Sep 17 00:00:00 2001 From: Eshel Yaron Date: Sun, 23 Jul 2023 20:37:31 +0300 Subject: [PATCH] Remove 'esy/init-step' wrappers --- .emacs.d/init.el | 2701 ++++++++++++++++++++++------------------------ 1 file changed, 1313 insertions(+), 1388 deletions(-) diff --git a/.emacs.d/init.el b/.emacs.d/init.el index f237966..04ce40b 100644 --- a/.emacs.d/init.el +++ b/.emacs.d/init.el @@ -12,149 +12,135 @@ (when (eq system-type 'darwin) (setq default-frame-alist '((fullscreen . fullboth)))) -(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 - bug-reference-url-format "https://debbugs.gnu.org/%s" - ;; save bookmarks immediately - bookmark-save-flag 1 - ;; show matches count in isearch prompt - isearch-lazy-count t - ;; kill Eat terminal buffers when their process exits - eat-kill-buffer-on-exit t - ;; don't spawn new frames in Ediff - ediff-window-setup-function #'ediff-setup-windows-plain - duplicate-line-final-position -1 - tramp-kubernetes-namespace "argo" - ;; 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 '( - avy - bbdb - corfu - debbugs - devdocs - diff-hl - eat - eglot - ef-themes - elfeed - embark-consult - emms - gnu-elpa-keyring-update - gnuplot - graphql-mode - graphviz-dot-mode - htmlize - ialign - keycast - kubernetes - lin - magit - markdown-mode - mastodon - ob-prolog - orderless - org - org-transclusion - osm - package-lint - paredit - pdf-tools - rainbow-delimiters - rainbow-mode - rg - smtpmail-multi - sqlformat - sweeprolog - terraform-mode - vterm - whitespace-cleanup-mode - ) - ;; configure Org capture templates - org-capture-templates '(("t" "Todo [inbox]" entry - (file+headline "~/org/inbox.org" "Tasks") - "** TODO [#B] %^{Task} %^g +;;; 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)))) + +;;; 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 + bug-reference-url-format "https://debbugs.gnu.org/%s" + ;; save bookmarks immediately + bookmark-save-flag 1 + ;; show matches count in isearch prompt + isearch-lazy-count t + ;; kill Eat terminal buffers when their process exits + eat-kill-buffer-on-exit t + ;; don't spawn new frames in Ediff + ediff-window-setup-function #'ediff-setup-windows-plain + duplicate-line-final-position -1 + tramp-kubernetes-namespace "argo" + ;; 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 '( + avy + bbdb + corfu + debbugs + devdocs + diff-hl + eat + eglot + ef-themes + elfeed + embark-consult + emms + gnu-elpa-keyring-update + gnuplot + graphql-mode + graphviz-dot-mode + htmlize + ialign + keycast + kubernetes + lin + magit + markdown-mode + mastodon + ob-prolog + orderless + org + org-transclusion + osm + package-lint + paredit + pdf-tools + rainbow-delimiters + rainbow-mode + rg + smtpmail-multi + sqlformat + sweeprolog + terraform-mode + vterm + 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 @@ -162,1277 +148,1216 @@ :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) - ("adi" . ?a) - ("holland" . ?h) - ("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-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:" - ;; associate Org Babel languages with major modes - org-src-lang-modes '(("SWI-Prolog" . sweeprolog) - ("Dockerfile" . dockerfile-ts) - ("bash" . sh) - ("shell" . sh) - ("toml" . toml-ts)) - ;; preview LaTeX fragments in Org buffers via SVG - org-preview-latex-default-process 'dvisvgm - ;; don't auto-save remote files - remote-file-name-inhibit-auto-save t - ;; don't ask me about it either - tramp-allow-unsafe-temporary-files t - ;; increase maximum number of recent files Emacs remembers - recentf-max-saved-items 128 - ;; increase maximum kill ring size - kill-ring-max 256 - ;; save text copied from another program to the kill ring - save-interprogram-paste-before-kill 2048 - ;; have C-u followed by repeated C-SPC keep popping - set-mark-command-repeat-pop t - ;; 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)) - ef-themes-mixed-fonts t - ef-themes-variable-pitch-ui t - ef-themes-region nil - osm-copyright nil - ;; 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)) - ;; (uva . ("eshel.yaron@student.uva.nl" - ;; "smtp.office365.com" - ;; 587 - ;; "eshel.yaron@student.uva.nl" - ;; starttls nil nil nil)) - ) - ;; associate email addresses with SMTP accounts - smtpmail-multi-associations '(("eshelshay\.yaron@gmail\.com" esy) - ("eshel@swi-prolog\.org" swp) - ("me@eshelyaron\.com" me) - ;; ("eshel\.yaron@student\.uva\.nl" uva) + :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) + ("adi" . ?a) + ("holland" . ?h) + ("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-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:" + ;; associate Org Babel languages with major modes + org-src-lang-modes '(("SWI-Prolog" . sweeprolog) + ("Dockerfile" . dockerfile-ts) + ("bash" . sh) + ("shell" . sh) + ("toml" . toml-ts)) + ;; preview LaTeX fragments in Org buffers via SVG + org-preview-latex-default-process 'dvisvgm + ;; don't auto-save remote files + remote-file-name-inhibit-auto-save t + ;; don't ask me about it either + tramp-allow-unsafe-temporary-files t + ;; increase maximum number of recent files Emacs remembers + recentf-max-saved-items 128 + ;; increase maximum kill ring size + kill-ring-max 256 + ;; save text copied from another program to the kill ring + save-interprogram-paste-before-kill 2048 + ;; have C-u followed by repeated C-SPC keep popping + set-mark-command-repeat-pop t + ;; 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)) + ef-themes-mixed-fonts t + ef-themes-variable-pitch-ui t + ef-themes-region nil + osm-copyright nil + ;; 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)) + ;; (uva . ("eshel.yaron@student.uva.nl" + ;; "smtp.office365.com" + ;; 587 + ;; "eshel.yaron@student.uva.nl" + ;; starttls nil nil nil)) + ) + ;; associate email addresses with SMTP accounts + smtpmail-multi-associations '(("eshelshay\.yaron@gmail\.com" esy) + ("eshel@swi-prolog\.org" swp) + ("me@eshelyaron\.com" me) + ;; ("eshel\.yaron@student\.uva\.nl" uva) + ) + ;; 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 + read-mail-command 'gnus + ;; 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-cite-parse-max-size nil + gnus-face-1 'bold + gnus-face-2 'gnus-cite-1 + gnus-face-3 'shadow + gnus-summary-line-format "%1{%U%R%z%}%B%2{%~(form (replace-regexp-in-string \" via .*\" \"\" (gnus-summary-from-or-to-or-newsgroups gnus-tmp-header gnus-tmp-from)))@%} %3{(%&user-date;, %k)%}:%* %s\n" + gnus-simplify-subject-functions '(gnus-simplify-subject-re) + gnus-treat-display-smileys nil + 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)) + ;; (nnimap "uva" + ;; (nnimap-address "outlook.office365.com") + ;; (nnimap-server-port "imaps") + ;; (nnimap-stream ssl)) ) - ;; 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 - read-mail-command 'gnus - ;; 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-cite-parse-max-size nil - gnus-face-1 'bold - gnus-face-2 'gnus-cite-1 - gnus-face-3 'shadow - gnus-summary-line-format "%1{%U%R%z%}%B%2{%~(form (replace-regexp-in-string \" via .*\" \"\" (gnus-summary-from-or-to-or-newsgroups gnus-tmp-header gnus-tmp-from)))@%} %3{(%&user-date;, %k)%}:%* %s\n" - gnus-simplify-subject-functions '(gnus-simplify-subject-re) - gnus-treat-display-smileys nil - 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)) - ;; (nnimap "uva" - ;; (nnimap-address "outlook.office365.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 - 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) - ;; free-style numbering plan - bbdb-phone-style nil - ;; allow cycling through mail address completion candidate - bbdb-complete-mail-allow-cycling t - ;; don't pop up BBDB records after completing mail addresses - bbdb-completion-display-record nil - eww-auto-rename-buffer 'title - browse-url-browser-function #'eww-browse-url - browse-url-generic-program "open" - global-auto-revert-non-file-buffers t - auto-revert-verbose nil - query-about-changed-file t - ;; show flymake diagnostics as overlays at eol - ;; flymake-show-diagnostics-at-end-of-line 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 - completion-styles '(orderless partial-completion basic) - completion-show-help nil - ;; completions-header-format nil - ;; completion-auto-help 'visible - 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 - ;; include CWD in shell command prompts - shell-command-prompt-show-cwd t - 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) - ;; IRC stuff - rcirc-default-nick "esy" - rcirc-server-alist '(("irc.libera.chat" - :channels ("#emacs") - :port 6697 - :encryption tls)) - rcirc-log-flag t - ;; use my custom project-prompting function - project-prompter #'esy/read-project-by-name - ;; have common bindings initially hidden in the output of C-h b - describe-bindings-outline-rules `((match-regexp - . - ,(regexp-opt - '("Key translations" - "Global Bindings:" - "Function key map translations" - "pixel-scroll-precision-mode" - "context-menu-mode"))))) - (setq-default indent-tabs-mode nil)) - -(esy/init-step theme - "Load and enable a custom theme." - ;; (require-theme 'modus-themes) - ;; (load-theme 'modus-vivendi) - (require 'ef-themes) - - (defvar esy/ef-themes-ring (make-ring 16)) - - (defun esy/ef-themes-load-previous () - (interactive) - (when (< 0 (ring-length esy/ef-themes-ring)) - (let ((theme (ring-remove esy/ef-themes-ring 0))) - (message "Loaded `%s'" theme) - (ef-themes--load-theme theme)))) - - (defun esy/ef-themes-load-random () - (interactive) - (let* ((theme (ef-themes--current-theme)) - (themes (seq-filter - (lambda (other-theme) - (not (equal other-theme theme))) - '( - ef-autumn - ef-bio - ef-cherie - ef-cyprus - ef-dark - ef-day - ef-duo-dark - ef-duo-light - ef-elea-dark - ef-elea-light - ef-frost - ef-kassio - ef-light - ef-maris-dark - ef-maris-light - ef-night - ef-spring - ef-summer - ef-symbiosis - ef-trio-dark - ef-trio-light - ef-winter - ))) - (n (random (length themes))) - (pick (nth n themes)) - (loaded (if (null pick) (car themes) pick))) - (ring-insert esy/ef-themes-ring theme) - (ef-themes--load-theme loaded) - (message "Loaded `%s'" loaded))) - - (esy/ef-themes-load-random)) - -(esy/init-step fonts - "Set up the Iosevka font family." - (when (eq system-type 'darwin) - (set-face-attribute 'default nil :height 130)) - (set-face-attribute 'default nil :family "Iosevka") - (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)))) - - (defvar duplicate-line-final-position) - - (defun duplicate-line-stay (arg) - (interactive "p") - (let ((duplicate-line-final-position 0)) - (duplicate-line arg))) - - (defun esy/ttyper () - (interactive) - (eat "ttyper" t)) - - (defun esy/hut-builds () - (interactive) - (let ((eat-kill-buffer-on-exit nil)) - (eat "hut builds show -f" t))) - - (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)) - - (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)) - - (defun esy/recent-log-summary () - "Display a summary of my website's most recent access log." - (interactive) - (async-shell-command (string-join (list "ssh" - "root@direct.eshelyaron.com" - (shell-quote-argument - (string-join '("grep -E '\"GET.+\" 2' /var/log/apache2/access.log" - "tr -d '\"'" - "tr -d \"'\"" - "cut -f 7,11,12 -d ' '" - "sort" - "uniq -c" - "sort -n") - " | "))) - " ") - "*Recent Log Summary*")) - - (defun esy/access-log-summary () - "Display a summary of my website's access log." - (interactive) - (let ((default-directory "/ssh:root@direct.eshelyaron.com:/var/log/apache2/")) - (async-shell-command (string-join '("zcat access.log.*.gz" - "cat - access.log access.log.1" - "grep -E '\"GET.+\" 2'" - "tr -d '\"'" - "tr -d \"'\"" - "cut -f 7,11,12 -d ' '" - "sort" - "uniq -c" - "sort -n") - " | ") - "*Access Log Summary*"))) - - (defun esy/clone (remote) - (interactive (list (let ((default (thing-at-point 'url))) - (read-string (format-prompt "Clone" default) - nil nil default)))) - (let ((dir (expand-file-name - (file-name-base (car (url-path-and-query - (url-generic-parse-url remote)))) - "~/checkouts"))) - (vc-clone remote 'Git dir) - (find-file dir))) - - (defun esy/json-path-to-position (pos) - "Return the JSON path from the document's root to the element at POS. + ;; 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 + 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) + ;; free-style numbering plan + bbdb-phone-style nil + ;; allow cycling through mail address completion candidate + bbdb-complete-mail-allow-cycling t + ;; don't pop up BBDB records after completing mail addresses + bbdb-completion-display-record nil + eww-auto-rename-buffer 'title + browse-url-browser-function #'eww-browse-url + browse-url-generic-program "open" + global-auto-revert-non-file-buffers t + auto-revert-verbose nil + query-about-changed-file t + ;; show flymake diagnostics as overlays at eol + ;; flymake-show-diagnostics-at-end-of-line 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 + completion-styles '(orderless partial-completion basic) + completion-show-help nil + ;; completions-header-format nil + ;; completion-auto-help 'visible + 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 + ;; include CWD in shell command prompts + shell-command-prompt-show-cwd t + 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) + ;; IRC stuff + rcirc-default-nick "esy" + rcirc-server-alist '(("irc.libera.chat" + :channels ("#emacs") + :port 6697 + :encryption tls)) + rcirc-log-flag t + ;; use my custom project-prompting function + project-prompter #'esy/read-project-by-name + ;; have common bindings initially hidden in the output of C-h b + describe-bindings-outline-rules `((match-regexp + . + ,(regexp-opt + '("Key translations" + "Global Bindings:" + "Function key map translations" + "pixel-scroll-precision-mode" + "context-menu-mode"))))) +(setq-default indent-tabs-mode nil) + +;;; Load and enable a custom theme +;; (require-theme 'modus-themes) +;; (load-theme 'modus-vivendi) +(require 'ef-themes) + +(defvar esy/ef-themes-ring (make-ring 16)) + +(defun esy/ef-themes-load-previous () + (interactive) + (when (< 0 (ring-length esy/ef-themes-ring)) + (let ((theme (ring-remove esy/ef-themes-ring 0))) + (message "Loaded `%s'" theme) + (ef-themes--load-theme theme)))) + +(defun esy/ef-themes-load-random () + (interactive) + (let* ((theme (ef-themes--current-theme)) + (themes (seq-filter + (lambda (other-theme) + (not (equal other-theme theme))) + '( + ef-autumn + ef-bio + ef-cherie + ef-cyprus + ef-dark + ef-day + ef-duo-dark + ef-duo-light + ef-elea-dark + ef-elea-light + ef-frost + ef-kassio + ef-light + ef-maris-dark + ef-maris-light + ef-night + ef-spring + ef-summer + ef-symbiosis + ef-trio-dark + ef-trio-light + ef-winter + ))) + (n (random (length themes))) + (pick (nth n themes)) + (loaded (if (null pick) (car themes) pick))) + (ring-insert esy/ef-themes-ring theme) + (ef-themes--load-theme loaded) + (message "Loaded `%s'" loaded))) + +(esy/ef-themes-load-random) + +;;; Set up the Iosevka font family +(when (eq system-type 'darwin) + (set-face-attribute 'default nil :height 130)) +(set-face-attribute 'default nil :family "Iosevka") +(set-face-attribute 'fixed-pitch nil :family "Iosevka") +(set-face-attribute 'variable-pitch nil :family "Iosevka Etoile") + +;;; 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) + +;;; 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)))) + +(defvar duplicate-line-final-position) + +(defun duplicate-line-stay (arg) + (interactive "p") + (let ((duplicate-line-final-position 0)) + (duplicate-line arg))) + +(defun esy/ttyper () + (interactive) + (eat "ttyper" t)) + +(defun esy/hut-builds () + (interactive) + (let ((eat-kill-buffer-on-exit nil)) + (eat "hut builds show -f" t))) + +(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)) + +(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)) + +(defun esy/recent-log-summary () + "Display a summary of my website's most recent access log." + (interactive) + (async-shell-command (string-join (list "ssh" + "root@direct.eshelyaron.com" + (shell-quote-argument + (string-join '("grep -E '\"GET.+\" 2' /var/log/apache2/access.log" + "tr -d '\"'" + "tr -d \"'\"" + "cut -f 7,11,12 -d ' '" + "sort" + "uniq -c" + "sort -n") + " | "))) + " ") + "*Recent Log Summary*")) + +(defun esy/access-log-summary () + "Display a summary of my website's access log." + (interactive) + (let ((default-directory "/ssh:root@direct.eshelyaron.com:/var/log/apache2/")) + (async-shell-command (string-join '("zcat access.log.*.gz" + "cat - access.log access.log.1" + "grep -E '\"GET.+\" 2'" + "tr -d '\"'" + "tr -d \"'\"" + "cut -f 7,11,12 -d ' '" + "sort" + "uniq -c" + "sort -n") + " | ") + "*Access Log Summary*"))) + +(defun esy/clone (remote) + (interactive (list (let ((default (thing-at-point 'url))) + (read-string (format-prompt "Clone" default) + nil nil default)))) + (let ((dir (expand-file-name + (file-name-base (car (url-path-and-query + (url-generic-parse-url remote)))) + "~/checkouts"))) + (vc-clone remote 'Git dir) + (find-file dir))) + +(defun esy/json-path-to-position (pos) + "Return the JSON path from the document's root to the element at POS. The path is represented as a list of strings and integers, corresponding to the object keys and array indices that lead from the root to the element at POS." - (named-let loop ((node (treesit-node-at pos)) (acc nil)) - (if-let ((parent (treesit-parent-until - node - (lambda (n) - (member (treesit-node-type n) - '("pair" "array")))))) - (loop parent - (cons - (pcase (treesit-node-type parent) - ("pair" - (treesit-node-text - (treesit-node-child (treesit-node-child parent 0) 1) t)) - ("array" - (named-let check ((i 1)) - (if (< pos (treesit-node-end (treesit-node-child parent i))) - (/ (1- i) 2) - (check (+ i 2)))))) - acc)) - acc))) - - (defun esy/json-path-at-point (point &optional kill) - "Display the JSON path at POINT. When KILL is non-nil, kill it too. + (named-let loop ((node (treesit-node-at pos)) (acc nil)) + (if-let ((parent (treesit-parent-until + node + (lambda (n) + (member (treesit-node-type n) + '("pair" "array")))))) + (loop parent + (cons + (pcase (treesit-node-type parent) + ("pair" + (treesit-node-text + (treesit-node-child (treesit-node-child parent 0) 1) t)) + ("array" + (named-let check ((i 1)) + (if (< pos (treesit-node-end (treesit-node-child parent i))) + (/ (1- i) 2) + (check (+ i 2)))))) + acc)) + acc))) + +(defun esy/json-path-at-point (point &optional kill) + "Display the JSON path at POINT. When KILL is non-nil, kill it too. Interactively, POINT is point and KILL is the prefix argument." - (interactive "d\nP" json-ts-mode) - (let ((path (mapconcat (lambda (o) (format "%s" o)) - (esy/json-path-to-position point) - "."))) - (if kill - (progn (kill-new path) (message "Copied: %s" path)) - (message path)) - path)) - - (defun esy/transcribe () - (interactive) - (message "Recording...") - (let ((process - (start-process "ffmpeg" nil "ffmpeg" - "-f" "avfoundation" - "-i" "1:0" - "-ar" "16000" - "-y" - "/tmp/foo.wav"))) - (set-transient-map - (make-sparse-keymap) nil - (lambda () - (message "Stopping recording") - (interrupt-process process) - (accept-process-output process 1 nil t) - (message "Transcribing...") - (message - (string-trim-right - (string-trim-left - (with-temp-buffer - (call-process "whisper" - nil '(t nil) nil - "-m" - "/Users/eshelyaron/checkouts/whisper.cpp/models/ggml-base.en.bin" - "--no-timestamps" "-f" "/tmp/foo.wav") - (goto-char (point-min)) - (while (search-forward "[BLANK_AUDIO]" nil t) - (replace-match "" nil t)) - (buffer-string)))))) - "Recording... Press any key to stop"))) - - (defun esy/record (timeout) - (interactive "p") - (message "Recording...") - (call-process "ffmpeg" - nil nil nil - "-f" "avfoundation" - "-i" "1:0" "-t" - (number-to-string (max timeout 2)) - "-ar" "16000" "-y" "/tmp/foo.wav") - (openai-chat (string-trim-right - (string-trim-left - (with-temp-buffer - (call-process "whisper" - nil '(t nil) nil - "-m" - "/Users/eshelyaron/checkouts/whisper.cpp/models/ggml-base.en.bin" - "--no-timestamps" "-f" "/tmp/foo.wav") - (goto-char (point-min)) - (while (search-forward "[BLANK_AUDIO]" nil t) - (replace-match "" nil t)) - (buffer-string)))))) - - (defun esy/dedicate-window (window flag) - (interactive (list (get-buffer-window) (not current-prefix-arg))) - (message "Window is %s dedicated to buffer %s." - (if flag (if (window-dedicated-p) "already" "now") "no longer") - (buffer-name)) - (set-window-dedicated-p window flag)) - - (defun esy/bump (tag-prefix) - (interactive (list "v")) - (require 'lisp-mnt) - (require 'magit-apply) - (let ((date (format-time-string "%F" (current-time))) - (current-version (save-excursion (lm-header "package-version")))) - (unless current-version - (user-error "No Package-Version Elisp header found")) - (let ((next-version (mapconcat #'number-to-string - (pcase (version-to-list - current-version) - (`(,major ,minor ,patch) - (list major (1+ minor) patch))) - "."))) - (with-current-buffer (find-file "NEWS.org") - (goto-char (point-min)) - (re-search-forward (concat "^\\(" org-outline-regexp "\\)") nil t) - (beginning-of-line) - (if (looking-at (rx "* Version " - (group-n 1 - (+ digit) "." - (+ digit) "." - (+ digit)) - " in development")) - (progn - (setq next-version (match-string-no-properties 1)) - (end-of-line) - (delete-char -14) - (insert "on " date) - (forward-char 2)) - (insert "* Version " next-version " on " date "\n\n"))) - (lm-header "package-version") - (delete-region (point) (pos-eol)) - (insert next-version) - (vc-print-root-log) - (find-file-other-window "NEWS.org") - (named-let loop () - (recursive-edit) - (unless (y-or-n-p "OK to stage, commit and tag the new version?") - (loop))) - (unless (= (call-process "git" nil nil nil - "add" (buffer-file-name) "NEWS.org") - 0) - (error "Git add failed")) - (unless (= (call-process "git" nil nil nil - "commit" "-m" - (concat "Announce recent changes " - "in NEWS.org " - "and bump version to " - next-version)) - 0) - (error "Git commit failed")) - (unless (= (call-process "git" nil nil nil - "tag" "-s" "-m" - (concat "Release version " - next-version) - (concat tag-prefix next-version)) - 0) - (error "Git tag failed")))))) - -(esy/init-step auto-exec-permissions - "Ensure scripts ran with `executable-interpret' are executable." - (with-eval-after-load 'executable - (define-advice executable-interpret (:before (&rest _) ensure-executable) - (unless (file-exists-p buffer-file-name) - (basic-save-buffer)) - (executable-make-buffer-file-executable-if-script-p)))) - -(esy/init-step pulse-on-window-selection-change - "Pulse the line around point after switching windows." - - (defun esy/pulse-on-window-selection-change (&rest _) - (pulse-momentary-highlight-one-line)) - - (add-hook 'window-selection-change-functions - #'esy/pulse-on-window-selection-change)) - -(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)) - -(esy/init-step bindings - "Bind some keys." - (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-publish-create-post) - (keymap-global-set "C-c O" #'openai-chat) - (keymap-global-set "C-c E" #'elfeed) - (keymap-global-set "C-c T" #'esy/ttyper) - (keymap-global-set "C-c R" #'esy/record) - (keymap-global-set "C-c G" #'gnus) - (keymap-global-set "C-c M" #'mastodon) - (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 "C-c 1" #'delete-other-windows) - (keymap-global-set "C-c 2" #'split-window-below) - (keymap-global-set "C-c 3" #'split-window-right) - (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 "s-n" #'duplicate-line) - (keymap-global-set "s-p" #'duplicate-line-stay) - (keymap-global-set "s-u" #'universal-argument) - (keymap-global-set "s--" #'negative-argument) - (keymap-global-set "M-#" #'dictionary-search) - (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-global-set "\"" #'insert-pair) - (keymap-global-set "" #'esy/ef-themes-load-random) - (keymap-global-set "s-]" #'esy/ef-themes-load-random) - (keymap-global-set "s-[" #'esy/ef-themes-load-previous) - - (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) - - (keymap-set window-prefix-map "p" #'windmove-swap-states-up) - (keymap-set window-prefix-map "n" #'windmove-swap-states-down) - (keymap-set window-prefix-map "a" #'windmove-swap-states-left) - (keymap-set window-prefix-map "e" #'windmove-swap-states-right) - (keymap-set window-prefix-map "d" #'esy/dedicate-window) - - (dolist (command '(windmove-swap-states-up - windmove-swap-states-down - windmove-swap-states-left - windmove-swap-states-right)) - (put command 'repeat-map 'window-prefix-map)) - - ;; digit arguments with the super modifier - (dotimes (i 10) - (keymap-global-set (concat "s-" (number-to-string i)) - #'digit-argument)) - - ;; unbind some keys - (dolist (key '("s-a" "s-d" "s-e" "s-f" "s-g" "s-h" "s-j" "s-k" "s-l" - "s-m" "s-o" "s-q" "s-s" "s-t" "s-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)) - - (defvar esy/project-name-history nil) - - (defvar esy/projects-directory "~/checkouts/") - - (defun esy/read-project-by-name () - "Read a project name and return its root directory. + (interactive "d\nP" json-ts-mode) + (let ((path (mapconcat (lambda (o) (format "%s" o)) + (esy/json-path-to-position point) + "."))) + (if kill + (progn (kill-new path) (message "Copied: %s" path)) + (message path)) + path)) + +(defun esy/transcribe () + (interactive) + (message "Recording...") + (let ((process + (start-process "ffmpeg" nil "ffmpeg" + "-f" "avfoundation" + "-i" "1:0" + "-ar" "16000" + "-y" + "/tmp/foo.wav"))) + (set-transient-map + (make-sparse-keymap) nil + (lambda () + (message "Stopping recording") + (interrupt-process process) + (accept-process-output process 1 nil t) + (message "Transcribing...") + (message + (string-trim-right + (string-trim-left + (with-temp-buffer + (call-process "whisper" + nil '(t nil) nil + "-m" + "/Users/eshelyaron/checkouts/whisper.cpp/models/ggml-base.en.bin" + "--no-timestamps" "-f" "/tmp/foo.wav") + (goto-char (point-min)) + (while (search-forward "[BLANK_AUDIO]" nil t) + (replace-match "" nil t)) + (buffer-string)))))) + "Recording... Press any key to stop"))) + +(defun esy/record (timeout) + (interactive "p") + (message "Recording...") + (call-process "ffmpeg" + nil nil nil + "-f" "avfoundation" + "-i" "1:0" "-t" + (number-to-string (max timeout 2)) + "-ar" "16000" "-y" "/tmp/foo.wav") + (openai-chat (string-trim-right + (string-trim-left + (with-temp-buffer + (call-process "whisper" + nil '(t nil) nil + "-m" + "/Users/eshelyaron/checkouts/whisper.cpp/models/ggml-base.en.bin" + "--no-timestamps" "-f" "/tmp/foo.wav") + (goto-char (point-min)) + (while (search-forward "[BLANK_AUDIO]" nil t) + (replace-match "" nil t)) + (buffer-string)))))) + +(defun esy/dedicate-window (window flag) + (interactive (list (get-buffer-window) (not current-prefix-arg))) + (message "Window is %s dedicated to buffer %s." + (if flag (if (window-dedicated-p) "already" "now") "no longer") + (buffer-name)) + (set-window-dedicated-p window flag)) + +(defun esy/bump (tag-prefix) + (interactive (list "v")) + (require 'lisp-mnt) + (require 'magit-apply) + (let ((date (format-time-string "%F" (current-time))) + (current-version (save-excursion (lm-header "package-version")))) + (unless current-version + (user-error "No Package-Version Elisp header found")) + (let ((next-version (mapconcat #'number-to-string + (pcase (version-to-list + current-version) + (`(,major ,minor ,patch) + (list major (1+ minor) patch))) + "."))) + (with-current-buffer (find-file "NEWS.org") + (goto-char (point-min)) + (re-search-forward (concat "^\\(" org-outline-regexp "\\)") nil t) + (beginning-of-line) + (if (looking-at (rx "* Version " + (group-n 1 + (+ digit) "." + (+ digit) "." + (+ digit)) + " in development")) + (progn + (setq next-version (match-string-no-properties 1)) + (end-of-line) + (delete-char -14) + (insert "on " date) + (forward-char 2)) + (insert "* Version " next-version " on " date "\n\n"))) + (lm-header "package-version") + (delete-region (point) (pos-eol)) + (insert next-version) + (vc-print-root-log) + (find-file-other-window "NEWS.org") + (named-let loop () + (recursive-edit) + (unless (y-or-n-p "OK to stage, commit and tag the new version?") + (loop))) + (unless (= (call-process "git" nil nil nil + "add" (buffer-file-name) "NEWS.org") + 0) + (error "Git add failed")) + (unless (= (call-process "git" nil nil nil + "commit" "-m" + (concat "Announce recent changes " + "in NEWS.org " + "and bump version to " + next-version)) + 0) + (error "Git commit failed")) + (unless (= (call-process "git" nil nil nil + "tag" "-s" "-m" + (concat "Release version " + next-version) + (concat tag-prefix next-version)) + 0) + (error "Git tag failed"))))) + +;;; Ensure scripts ran with `executable-interpret' are executable +(with-eval-after-load 'executable + (define-advice executable-interpret (:before (&rest _) ensure-executable) + (unless (file-exists-p buffer-file-name) + (basic-save-buffer)) + (executable-make-buffer-file-executable-if-script-p))) + +;;; Pulse the line around point after switching windows + +(defun esy/pulse-on-window-selection-change (&rest _) + (pulse-momentary-highlight-one-line)) + +(add-hook 'window-selection-change-functions + #'esy/pulse-on-window-selection-change) + +;;; 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 ))) + +;;; Ensure external packages are installed +(package-install-selected-packages) + +;;; 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) + +;;; Extend other standard hooks +(add-hook 'text-mode-hook #'flyspell-mode) + +;;; Bind some keys +(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-publish-create-post) +(keymap-global-set "C-c O" #'openai-chat) +(keymap-global-set "C-c E" #'elfeed) +(keymap-global-set "C-c T" #'esy/ttyper) +(keymap-global-set "C-c R" #'esy/record) +(keymap-global-set "C-c G" #'gnus) +(keymap-global-set "C-c M" #'mastodon) +(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 "C-c 1" #'delete-other-windows) +(keymap-global-set "C-c 2" #'split-window-below) +(keymap-global-set "C-c 3" #'split-window-right) +(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 "s-n" #'duplicate-line) +(keymap-global-set "s-p" #'duplicate-line-stay) +(keymap-global-set "s-u" #'universal-argument) +(keymap-global-set "s--" #'negative-argument) +(keymap-global-set "M-#" #'dictionary-search) +(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-global-set "\"" #'insert-pair) +(keymap-global-set "" #'esy/ef-themes-load-random) +(keymap-global-set "s-]" #'esy/ef-themes-load-random) +(keymap-global-set "s-[" #'esy/ef-themes-load-previous) + +(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) + +(keymap-set window-prefix-map "p" #'windmove-swap-states-up) +(keymap-set window-prefix-map "n" #'windmove-swap-states-down) +(keymap-set window-prefix-map "a" #'windmove-swap-states-left) +(keymap-set window-prefix-map "e" #'windmove-swap-states-right) +(keymap-set window-prefix-map "d" #'esy/dedicate-window) + +(dolist (command '(windmove-swap-states-up + windmove-swap-states-down + windmove-swap-states-left + windmove-swap-states-right)) + (put command 'repeat-map 'window-prefix-map)) + +;; digit arguments with the super modifier +(dotimes (i 10) + (keymap-global-set (concat "s-" (number-to-string i)) + #'digit-argument)) + +;; unbind some keys +(dolist (key '("s-a" "s-d" "s-e" "s-f" "s-g" "s-h" "s-j" "s-k" "s-l" + "s-m" "s-o" "s-q" "s-s" "s-t" "s-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) + +;;; 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)) + + (defvar esy/project-name-history nil) + + (defvar esy/projects-directory "~/checkouts/") + + (defun esy/read-project-by-name () + "Read a project name and return its root directory. If no known project matches the selected name, prompt for a sub-directory of `esy/projects-directory' using the selected name as the initial input for completion, and return that directory." - (let* ((name-dir-alist - (delete - nil - (mapcar (lambda (dir) - (when-let ((proj (project-current nil dir))) - (cons (project-name proj) dir))) - (project-known-project-roots)))) - (current (project-current)) - (default (and current (project-name current))) - (name (completing-read (format-prompt "Project" default) - name-dir-alist - nil nil nil - 'esy/project-name-history - default))) - (or (alist-get name name-dir-alist nil nil #'string=) - (let* ((dir (read-directory-name "Project root directory: " - esy/projects-directory - nil t name)) - (project (project-current nil dir))) - (when project (project-remember-project project)) - dir)))))) - -(esy/init-step sql - "Configure SQL connections." - (with-eval-after-load 'sql - (defun esy/update-sql-connection-alist () - (interactive) - (auth-source-forget-all-cached) - (setq sql-connection-alist (delete nil - (mapcar (lambda (source) - (pcase (split-string (plist-get source :host) - (rx "^")) - (`(,con ,db ,host) - (list (intern con) - (list 'sql-product ''postgres) - (list 'sql-user (plist-get source :user)) - (list 'sql-port (string-to-number (plist-get source :port))) - (list 'sql-password (funcall (plist-get source :secret))) - (list 'sql-server host) - (list 'sql-database db))))) - (auth-source-search :port 5432 :max 10))))) - (esy/update-sql-connection-alist) - (add-hook 'sql-interactive-mode-hook #'toggle-truncate-lines) - (add-hook 'sql-interactive-mode-hook #'abbrev-mode) - (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 - (keymap-unset org-mode-map "C-," t) - (esy-publish-setup)) - - (with-eval-after-load 'org-agenda - (add-to-list 'org-agenda-custom-commands - '("w" "Work TODOs" tags-todo "+work")) - (add-to-list 'org-agenda-custom-commands - '("h" "Holland TODOs" tags-todo "+holland")))) - -(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) - (bbdb-initialize 'gnus 'mail 'message))) - -(esy/init-step dired - "Configure `dired'." - (with-eval-after-load 'dired - (put 'dired-find-alternate-file 'disabled nil))) - -(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)) - (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)))))) - -(esy/init-step proced - "Configure `proced'." - (with-eval-after-load 'proced - (add-hook 'proced-mode-hook (lambda () - (setq proced-auto-update-flag t))))) - -(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) - (keymap-unset flyspell-mode-map "C-M-i" t))) - -(esy/init-step minibuffer-completion - "Configure minibuffer completions." - - (defvar esy/completing-read-commands nil) - - (add-to-list 'savehist-additional-variables - 'esy/completing-read-commands) - - (define-advice completing-read (:before (&rest _) record-command) - (cl-incf (alist-get this-command esy/completing-read-commands 0))) - - (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))) - - (dolist (key-binding '(("C-p" . minibuffer-previous-completion) - ("C-n" . minibuffer-next-completion) - ("M-j" . minibuffer-force-complete-and-exit))) - (keymap-set minibuffer-local-completion-map - (car key-binding) - (cdr key-binding))) - - (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) - (pcase (let ((inhibit-message t)) - (ignore-errors (dabbrev-capf))) - (`(,beg ,end ,table . ,_) - (list beg end table :exclusive 'no)))) - - (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 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)) - ('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 - 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-fofrward - "k" #'emms-seek - "m" #'emms-show - "p" #'emms-pause - "s" #'emms-start - "t" #'emms-seek-to) - - (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)) - (put command 'repeat-map 'esy/emms-map)) - - (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 - (when (eq system-type 'darwin) - (setq vterm-shell "/bin/zsh")) - (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'." - (setq-default prolog-system 'swi) - (add-to-list 'major-mode-remap-alist '(prolog-mode . sweeprolog-mode)) - (with-eval-after-load 'sweeprolog - (setq sweeprolog-top-level-persistent-history - (locate-user-emacs-file ".sweep_history")) - (keymap-global-set "C-c p" sweeprolog-prefix-map) - (add-hook 'sweeprolog-mode-hook #'sweeprolog-electric-layout-mode) - (add-hook 'sweeprolog-top-level-mode-hook #'compilation-shell-minor-mode))) - -(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) - ("\\.toml\\'" . toml-ts-mode) - ("\\.json\\'" . json-ts-mode) - ("\\.tfstate\\'" . json-ts-mode) - ("\\.ts\\'" . typescript-ts-mode) - ("\\.rb\\'" . ruby-ts-mode) - ("\\.rs\\'" . rust-ts-mode) - ("\\.go\\'" . go-ts-mode) - ("\\.pdf\\'" . pdf-view-mode) - ("\\.plt?\\'" . prolog-mode))) - (push cell auto-mode-alist))) - -(esy/init-step kill-hook - "Compile this file (if changed) when Emacs is killed." - (defun esy/compile-config () + (let* ((name-dir-alist + (delete + nil + (mapcar (lambda (dir) + (when-let ((proj (project-current nil dir))) + (cons (project-name proj) dir))) + (project-known-project-roots)))) + (current (project-current)) + (default (and current (project-name current))) + (name (completing-read (format-prompt "Project" default) + name-dir-alist + nil nil nil + 'esy/project-name-history + default))) + (or (alist-get name name-dir-alist nil nil #'string=) + (let* ((dir (read-directory-name "Project root directory: " + esy/projects-directory + nil t name)) + (project (project-current nil dir))) + (when project (project-remember-project project)) + dir))))) + +;;; Configure SQL connections +(with-eval-after-load 'sql + (defun esy/update-sql-connection-alist () (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)) - -(esy/init-step help - "Configure Help." - (with-eval-after-load 'help-fns - (with-eval-after-load 'shortdoc - (add-hook 'help-fns-describe-function-functions - #'shortdoc-help-fns-examples-function)))) - -(esy/init-step dict - "Configure dictionary." - (with-eval-after-load 'dictionary - (setopt dictionary-search-interface 'help - dictionary-default-dictionary "gcide" - dictionary-default-strategy "prefix" - dictionary-server "dict.org"))) - -(esy/init-step elfeed - "Configure news feeds." - (with-eval-after-load 'elfeed - (keymap-set elfeed-show-mode-map "S-SPC" #'scroll-down-command) - - (defvar esy/feeds-file (locate-user-emacs-file "feeds.eld")) - - (defun esy/feeds () + (auth-source-forget-all-cached) + (setq sql-connection-alist (delete nil + (mapcar (lambda (source) + (pcase (split-string (plist-get source :host) + (rx "^")) + (`(,con ,db ,host) + (list (intern con) + (list 'sql-product ''postgres) + (list 'sql-user (plist-get source :user)) + (list 'sql-port (string-to-number (plist-get source :port))) + (list 'sql-password (funcall (plist-get source :secret))) + (list 'sql-server host) + (list 'sql-database db))))) + (auth-source-search :port 5432 :max 10))))) + (esy/update-sql-connection-alist) + (add-hook 'sql-interactive-mode-hook #'toggle-truncate-lines) + (add-hook 'sql-interactive-mode-hook #'abbrev-mode) + (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)))) + +;;; Configure Org mode +(with-eval-after-load 'org + (keymap-unset org-mode-map "C-," t) + (esy-publish-setup)) + +(with-eval-after-load 'org-agenda + (add-to-list 'org-agenda-custom-commands + '("w" "Work TODOs" tags-todo "+work")) + (add-to-list 'org-agenda-custom-commands + '("h" "Holland TODOs" tags-todo "+holland"))) + +;;; 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) + (bbdb-initialize 'gnus 'mail 'message)) + +;;; Configure `dired' +(with-eval-after-load 'dired + (put 'dired-find-alternate-file 'disabled nil)) + +;;; 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)) + (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))))) + +;;; Configure `proced' +(with-eval-after-load 'proced + (add-hook 'proced-mode-hook (lambda () + (setq proced-auto-update-flag t)))) + +;;; Configure `world-clock' +(with-eval-after-load 'time + (add-to-list 'zoneinfo-style-world-list '("Europe/Amsterdam" "Amsterdam"))) + +;;; 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) + (keymap-unset flyspell-mode-map "C-M-i" t)) + +;;; Configure minibuffer completions + +(defvar esy/completing-read-commands nil) + +(add-to-list 'savehist-additional-variables + 'esy/completing-read-commands) + +(define-advice completing-read (:before (&rest _) record-command) + (cl-incf (alist-get this-command esy/completing-read-commands 0))) + +(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))) + +(dolist (key-binding '(("C-p" . minibuffer-previous-completion) + ("C-n" . minibuffer-next-completion) + ("M-j" . minibuffer-force-complete-and-exit))) + (keymap-set minibuffer-local-completion-map + (car key-binding) + (cdr key-binding))) + +(with-eval-after-load 'consult + (with-eval-after-load 'embark + (require 'embark-consult))) + +;;; 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) + (pcase (let ((inhibit-message t)) + (ignore-errors (dabbrev-capf))) + (`(,beg ,end ,table . ,_) + (list beg end table :exclusive 'no)))) + +(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) + +;;; 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)) + +;;; 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)) + ('dabbrev (lambda (_) "… ")))) + (add-to-list 'corfu-margin-formatters #'esy/margin-formatter) + (corfu-indexed-mode)) + +;;; 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 + 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)) + +;;; 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-fofrward + "k" #'emms-seek + "m" #'emms-show + "p" #'emms-pause + "s" #'emms-start + "t" #'emms-seek-to) + +(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)) + (put command 'repeat-map 'esy/emms-map)) + +(with-eval-after-load 'emms + (emms-minimalistic)) + +;;; 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\\'") + +;;; Configure terminal emulation via `vterm' +(with-eval-after-load 'vterm + (when (eq system-type 'darwin) + (setq vterm-shell "/bin/zsh")) + (add-to-list 'vterm-tramp-shells '("kubernetes" "/bin/bash"))) + +;;; 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)) + +;;; Configure Prolog integration via `sweeprolog' +(setq-default prolog-system 'swi) +(add-to-list 'major-mode-remap-alist '(prolog-mode . sweeprolog-mode)) +(with-eval-after-load 'sweeprolog + (setq sweeprolog-top-level-persistent-history + (locate-user-emacs-file ".sweep_history")) + (keymap-global-set "C-c p" sweeprolog-prefix-map) + (add-hook 'sweeprolog-mode-hook #'sweeprolog-electric-layout-mode) + (add-hook 'sweeprolog-top-level-mode-hook #'compilation-shell-minor-mode)) + +;;; Configure recursive grepping via `rg' +(with-eval-after-load 'rg + (add-to-list 'rg-custom-type-aliases '("Prolog" . "*.pl *.plt *.pro *.prolog"))) + +;;; 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)) + +;;; Associate major modes with files based on their extensions +(dolist (cell '(("Dockerfile" . dockerfile-ts-mode) + ("\\.ya?ml\\'" . yaml-ts-mode) + ("\\.toml\\'" . toml-ts-mode) + ("\\.json\\'" . json-ts-mode) + ("\\.tfstate\\'" . json-ts-mode) + ("\\.ts\\'" . typescript-ts-mode) + ("\\.rb\\'" . ruby-ts-mode) + ("\\.rs\\'" . rust-ts-mode) + ("\\.go\\'" . go-ts-mode) + ("\\.pdf\\'" . pdf-view-mode) + ("\\.plt?\\'" . prolog-mode))) + (push cell auto-mode-alist)) + +;;; Configure Help +(with-eval-after-load 'help-fns + (with-eval-after-load 'shortdoc + (add-hook 'help-fns-describe-function-functions + #'shortdoc-help-fns-examples-function))) + +;;; Configure dictionary +(with-eval-after-load 'dictionary + (setopt dictionary-search-interface 'help + dictionary-default-dictionary "gcide" + dictionary-default-strategy "prefix" + dictionary-server "dict.org")) + +;;; Configure news feeds +(with-eval-after-load 'elfeed + (keymap-set elfeed-show-mode-map "S-SPC" #'scroll-down-command) + + (defvar esy/feeds-file (locate-user-emacs-file "feeds.eld")) + + (defun esy/feeds () + (with-temp-buffer + (insert-file-contents-literally esy/feeds-file) + (goto-char (point-min)) + (read (current-buffer)))) + + (defun esy/read-feed-keywords () + (mapcar #'intern + (completing-read-multiple + "Keywords: " + (let ((keywords nil)) + (dolist + (feed-keywords + (mapcar + #'cdr + (esy/feeds))) + (mapc (lambda (keyword) + (add-to-list 'keywords keyword)) + feed-keywords)) + (mapcar #'symbol-name keywords))))) + + (defun esy/add-feed (url keywords) + (interactive (let ((default (thing-at-point-url-at-point))) + (list (read-string (format-prompt "Feed URL" + default) + nil nil default) + (esy/read-feed-keywords)))) + (let ((feeds (cons (cons url keywords) (esy/feeds)))) (with-temp-buffer - (insert-file-contents-literally esy/feeds-file) - (goto-char (point-min)) - (read (current-buffer)))) - - (defun esy/read-feed-keywords () - (mapcar #'intern - (completing-read-multiple - "Keywords: " - (let ((keywords nil)) - (dolist - (feed-keywords - (mapcar - #'cdr - (esy/feeds))) - (mapc (lambda (keyword) - (add-to-list 'keywords keyword)) - feed-keywords)) - (mapcar #'symbol-name keywords))))) - - (defun esy/add-feed (url keywords) - (interactive (let ((default (thing-at-point-url-at-point))) - (list (read-string (format-prompt "Feed URL" - default) - nil nil default) - (esy/read-feed-keywords)))) - (let ((feeds (cons (cons url keywords) (esy/feeds)))) - (with-temp-buffer - (pp (cons (cons url keywords) (esy/feeds)) - (current-buffer)) - (write-region (point-min) - (point-max) - esy/feeds-file)) - (setq elfeed-feeds feeds))) - - (defun esy/update-feeds () - (interactive) - (setq elfeed-feeds (esy/feeds))) - - (with-eval-after-load 'eww - (defun esy/eww-add-feed (url keywords) - (interactive (list (eww-read-alternate-url) - (esy/read-feed-keywords)) - eww-mode) - (esy/add-feed url keywords))) - - (setq - ;; don't use bold face for unread Elfeed entries - elfeed-search-face-alist '((unread (default))) - ;; read feeds list from a separate file - elfeed-feeds (esy/feeds)))) - -(esy/init-step currency - "Track currency exchange rates." - - (defvar esy/eur-to-ils-rates nil) - - (add-to-list 'savehist-additional-variables - 'esy/eur-to-ils-rates) - - (defun esy/update-eur-to-ils-rate () - (require 'dom) - (let* ((time (float-time)) - (from "EUR") - (to "ILS") - (amount 1) - (url (url-parse-make-urlobj "https" - nil - nil - "www.x-rates.com" - nil - (concat "/calculator/?" - (url-build-query-string `(("from" ,from) - ("to" ,to) - ("amount" ,amount))))))) - (url-retrieve url - (lambda (_) - (goto-char (point-min)) - (search-forward "\n\n") - (push (cons time - (string-to-number (caddar (dom-by-class (libxml-parse-html-region (point)) - "ccOutputRslt")))) - esy/eur-to-ils-rates)) - nil t))) - - (run-at-time t 300 #'esy/update-eur-to-ils-rate) - - (defun esy/plot-eur-to-ils-rates (height width) - (require 'svg) - (let* ((svg (svg-create width height - :stroke "green")) - (time (float-time)) - (frame (* 60 60 24)) - (data (named-let loop ((rates esy/eur-to-ils-rates) - (acc nil) - (min most-positive-fixnum) - (max most-negative-fixnum)) - (if rates - (let ((x (* (/ (+ frame (- (caar rates) time)) - frame) - width))) - (if (< 0 x) - (let ((val (cdar rates))) - (loop (cdr rates) - (cons (cons x (cdar rates)) acc) - (min val min) - (max val max))) - (list acc min max))) - (list acc min max)))) - (points (nth 0 data)) - (min (nth 1 data)) - (max (nth 2 data)) - (normalized-points (mapcar (lambda (xy) - (cons (car xy) - (- height (* height - (/ (- (cdr xy) min) - (- max min)))))) - points))) - (svg-polyline svg normalized-points :fill-color "none") - (svg-image svg))) - - (defun esy/plot-eur-rates () + (pp (cons (cons url keywords) (esy/feeds)) + (current-buffer)) + (write-region (point-min) + (point-max) + esy/feeds-file)) + (setq elfeed-feeds feeds))) + + (defun esy/update-feeds () (interactive) - (with-current-buffer-window "EUR to ILS" nil nil - (insert-image (esy/plot-eur-to-ils-rates 300 500)) - (insert "\n"))) + (setq elfeed-feeds (esy/feeds))) - (push '(:eval (concat "1€=" (number-to-string (cdar esy/eur-to-ils-rates)) "₪ ")) - global-mode-string)) + (with-eval-after-load 'eww + (defun esy/eww-add-feed (url keywords) + (interactive (list (eww-read-alternate-url) + (esy/read-feed-keywords)) + eww-mode) + (esy/add-feed url keywords))) -(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))) + (setq + ;; don't use bold face for unread Elfeed entries + elfeed-search-face-alist '((unread (default))) + ;; read feeds list from a separate file + elfeed-feeds (esy/feeds))) + +;;; Track currency exchange rates + +(defvar esy/eur-to-ils-rates nil) + +(add-to-list 'savehist-additional-variables + 'esy/eur-to-ils-rates) + +(defun esy/update-eur-to-ils-rate () + (require 'dom) + (let* ((time (float-time)) + (from "EUR") + (to "ILS") + (amount 1) + (url (url-parse-make-urlobj "https" + nil + nil + "www.x-rates.com" + nil + (concat "/calculator/?" + (url-build-query-string `(("from" ,from) + ("to" ,to) + ("amount" ,amount))))))) + (url-retrieve url + (lambda (_) + (goto-char (point-min)) + (search-forward "\n\n") + (push (cons time + (string-to-number (caddar (dom-by-class (libxml-parse-html-region (point)) + "ccOutputRslt")))) + esy/eur-to-ils-rates)) + nil t))) + +(run-at-time t 300 #'esy/update-eur-to-ils-rate) + +(defun esy/plot-eur-to-ils-rates (height width) + (require 'svg) + (let* ((svg (svg-create width height + :stroke "green")) + (time (float-time)) + (frame (* 60 60 24)) + (data (named-let loop ((rates esy/eur-to-ils-rates) + (acc nil) + (min most-positive-fixnum) + (max most-negative-fixnum)) + (if rates + (let ((x (* (/ (+ frame (- (caar rates) time)) + frame) + width))) + (if (< 0 x) + (let ((val (cdar rates))) + (loop (cdr rates) + (cons (cons x (cdar rates)) acc) + (min val min) + (max val max))) + (list acc min max))) + (list acc min max)))) + (points (nth 0 data)) + (min (nth 1 data)) + (max (nth 2 data)) + (normalized-points (mapcar (lambda (xy) + (cons (car xy) + (- height (* height + (/ (- (cdr xy) min) + (- max min)))))) + points))) + (svg-polyline svg normalized-points :fill-color "none") + (svg-image svg))) + +(defun esy/plot-eur-rates () + (interactive) + (with-current-buffer-window "EUR to ILS" nil nil + (insert-image (esy/plot-eur-to-ils-rates 300 500)) + (insert "\n"))) + +(push '(:eval (concat "1€=" (number-to-string (cdar esy/eur-to-ils-rates)) "₪ ")) + global-mode-string) (provide 'init) ;;; init.el ends here -- 2.39.2