;;; init.el --- Init -*- lexical-binding: t; -*- ;;; Constants (defconst rs/notes-dir "~/NPersonal/" "Directory for notes, org files, and denote.") (defconst rs/mail-dir "~/mail" "Directory for mail.") (defconst rs/full-name "Ray Andrew" "Full name for email and git.") (defconst rs/email-uchicago "rayandrew@uchicago.edu" "UChicago email address.") (defconst rs/email-personal "raydreww@gmail.com" "Personal email address.") (defconst rs/mu-binary "/etc/profiles/per-user/rayandrew/bin/mu" "Path to mu binary (Nix).") (defconst rs/mbsync-command "~/dotfiles/bin/path-shim mbsync -a" "Command to sync mail.") ;;; Elpaca bootstrap (defvar elpaca-installer-version 0.11) (defvar elpaca-directory (expand-file-name "elpaca/" user-emacs-directory)) (defvar elpaca-builds-directory (expand-file-name "builds/" elpaca-directory)) (defvar elpaca-repos-directory (expand-file-name "repos/" elpaca-directory)) (defvar elpaca-order '(elpaca :repo "https://github.com/progfolio/elpaca.git" :ref nil :depth 1 :inherit ignore :files (:defaults "elpaca-test.el" (:exclude "extensions")) :build (:not elpaca--activate-package))) (let* ((repo (expand-file-name "elpaca/" elpaca-repos-directory)) (build (expand-file-name "elpaca/" elpaca-builds-directory)) (order (cdr elpaca-order)) (default-directory repo)) (add-to-list 'load-path (if (file-exists-p build) build repo)) (unless (file-exists-p repo) (make-directory repo t) (when (< emacs-major-version 28) (require 'subr-x)) (condition-case-unless-debug err (if-let* ((buffer (pop-to-buffer-same-window "*elpaca-bootstrap*")) ((zerop (apply #'call-process `("git" nil ,buffer t "clone" ,@(when-let* ((depth (plist-get order :depth))) (list (format "--depth=%d" depth) "--no-single-branch")) ,(plist-get order :repo) ,repo)))) ((zerop (call-process "git" nil buffer t "checkout" (or (plist-get order :ref) "--")))) (emacs (concat invocation-directory invocation-name)) ((zerop (call-process emacs nil buffer nil "-Q" "-L" "." "--batch" "--eval" "(byte-recompile-directory \".\" 0 'force)"))) ((require 'elpaca)) ((elpaca-generate-autoloads "elpaca" repo))) (progn (message "%s" (buffer-string)) (kill-buffer buffer)) (error "%s" (with-current-buffer buffer (buffer-string)))) ((error) (warn "%s" err) (delete-directory repo 'recursive)))) (unless (require 'elpaca-autoloads nil t) (require 'elpaca) (elpaca-generate-autoloads "elpaca" repo) (load "./elpaca-autoloads"))) (add-hook 'after-init-hook #'elpaca-process-queues) (elpaca `(,@elpaca-order)) (elpaca elpaca-use-package (elpaca-use-package-mode)) ;; Block until elpaca is ready ; (elpaca-wait) ;;; Font (defun rs/get-default-font () (cond ((eq system-type 'windows-nt) "Consolas-11") ((eq system-type 'gnu/linux) "Consolas-11") ((eq system-type 'darwin) "Consolas-12"))) (set-face-font 'default (rs/get-default-font)) ;;; Basic settings from minimal-emacs (setq native-comp-async-query-on-exit t) (setq read-answer-short t) (setq use-short-answers t) ;; Undo limits (setq undo-limit (* 13 160000) undo-strong-limit (* 13 240000) undo-outer-limit (* 13 24000000)) ;; Minibuffer (setq enable-recursive-minibuffers t) (setq minibuffer-prompt-properties '(read-only t intangible t cursor-intangible t face minibuffer-prompt)) (add-hook 'minibuffer-setup-hook #'cursor-intangible-mode) ;; UI updates (setq which-func-update-delay 1.0) (setq idle-update-delay 1.0) (defalias #'view-hello-file #'ignore) (setq visible-bell nil) (setq ring-bell-function #'ignore) ;; Show-paren (setq show-paren-delay 0.1 show-paren-highlight-openparen t show-paren-when-point-inside-paren t show-paren-when-point-in-periphery t) ;; Misc (setq custom-buffer-done-kill t) (setq whitespace-line-column nil) (setq-default display-line-numbers-width 3) (setq-default display-line-numbers-widen t) (setq truncate-string-ellipsis "…") (setq eval-expression-print-length nil eval-expression-print-level nil) (setq x-underline-at-descent-line t) (setq remote-file-name-inhibit-cache 50) (setq imenu-auto-rescan t) (setq imenu-max-item-length 160) (setq next-line-add-newlines nil) (setq bookmark-save-flag 1) ;; Tramp (setq tramp-verbose 1) (setq tramp-completion-reread-directory-timeout 50) ;; Files (setq delete-by-moving-to-trash (not noninteractive)) (setq remote-file-name-inhibit-delete-by-moving-to-trash t) (setq find-file-suppress-same-file-warnings t) (setq find-file-visit-truename t vc-follow-symlinks t) (setq split-width-threshold 170 split-height-threshold nil) ;; Buffers (setq uniquify-buffer-name-style 'forward) ;; Comint (setq ansi-color-for-comint-mode t comint-prompt-read-only t comint-buffer-maximum-size 4096) ;; Compilation (setq compilation-ask-about-save nil compilation-always-kill t compilation-scroll-output 'first-error) (setq confirm-nonexistent-file-or-buffer nil) ;; Backup files (setq create-lockfiles nil) (setq make-backup-files nil) (setq backup-directory-alist `(("." . ,(expand-file-name "backup" user-emacs-directory)))) (setq tramp-backup-directory-alist backup-directory-alist) (setq backup-by-copying-when-linked t) (setq backup-by-copying t) (setq delete-old-versions t) (setq version-control t) (setq kept-new-versions 5) (setq kept-old-versions 5) ;; VC (setq vc-git-print-log-follow t) (setq vc-make-backup-files nil) (setq vc-git-diff-switches '("--histogram")) ;; Auto save (setq auto-save-default nil) (setq auto-save-no-message t) (setq auto-save-include-big-deletions t) (setq auto-save-list-file-prefix (expand-file-name "autosave/" user-emacs-directory)) (setq tramp-auto-save-directory (expand-file-name "tramp-autosave/" user-emacs-directory)) (setq kill-buffer-delete-auto-save-files t) (setq kill-do-not-save-duplicates t) ;; Auto revert (setq revert-without-query (list ".") auto-revert-stop-on-user-input nil auto-revert-verbose t) (setq global-auto-revert-non-file-buffers t) (setq global-auto-revert-ignore-modes '(Buffer-menu-mode)) ;; Recentf (setq recentf-max-saved-items 300) (setq recentf-max-menu-items 15) (setq recentf-auto-cleanup 'mode) (setq recentf-exclude nil) ;; Saveplace (setq save-place-file (expand-file-name "saveplace" user-emacs-directory)) (setq save-place-limit 600) ;; Savehist (setq history-length 300) (setq savehist-save-minibuffer-history t) (setq savehist-additional-variables '(kill-ring register-alist mark-ring global-mark-ring search-ring regexp-search-ring)) ;; Frames and windows (setq window-resize-pixelwise nil) (setq resize-mini-windows 'grow-only) (setq window-divider-default-bottom-width 1 window-divider-default-places t window-divider-default-right-width 1) ;; Fontification (setq redisplay-skip-fontification-on-input t) ;; Scrolling (setq fast-but-imprecise-scrolling t) (setq scroll-error-top-bottom t) (setq scroll-preserve-screen-position t) (setq scroll-conservatively 20) (setq auto-window-vscroll nil) (setq scroll-margin 0) (setq next-screen-context-lines 0) (setq hscroll-margin 2 hscroll-step 1) ;; Mouse (setq mouse-yank-at-point nil) ;; Cursor (setq blink-matching-paren nil) (setq x-stretch-cursor nil) (setq-default cursor-in-non-selected-windows nil) (setq highlight-nonselected-windows nil) ;; Text editing (setq global-text-scale-adjust-resizes-frames nil) (setq delete-pair-blink-delay 0.03) (setq-default left-fringe-width 8) (setq-default right-fringe-width 8) (setq-default indicate-buffer-boundaries nil) (setq-default indicate-empty-lines nil) (setq-default word-wrap t) (setq-default truncate-lines t) (setq truncate-partial-width-windows nil) (setq-default electric-indent-chars '(?\n ?\^?)) (setq-default indent-tabs-mode nil tab-width 4) (setq tab-always-indent 'complete) (setq tab-first-completion 'word-or-paren-or-punct) (setq read-extended-command-predicate #'command-completion-default-include-p) (setq comment-multi-line t) (setq comment-empty-lines t) (setq-default fill-column 80) (setq sentence-end-double-space nil) (setq require-final-newline t) (setq lazy-highlight-initial-delay 0) ;; Modeline (setq display-time-default-load-average nil) ;; Filetype (setq python-indent-guess-indent-offset-verbose nil) (setq sh-indent-after-continuation 'always) ;; Dired and ls-lisp (setq dired-free-space nil dired-dwim-target t dired-deletion-confirmer 'y-or-n-p dired-filter-verbose nil dired-recursive-deletes 'top dired-recursive-copies 'always dired-vc-rename-file t dired-create-destination-dirs 'ask dired-clean-confirm-killing-deleted-buffers nil) (setq auto-revert-remote-files nil) (setq dired-auto-revert-buffer 'dired-buffer-stale-p) (setq dired-omit-verbose nil) (setq ls-lisp-verbosity nil) (setq ls-lisp-dirs-first t) ;; Ediff (setq ediff-window-setup-function 'ediff-setup-windows-plain ediff-split-window-function 'split-window-horizontally) ;; Help (setq apropos-do-all t) (setq help-enable-completion-autoload nil) (setq help-enable-autoload nil) (setq help-enable-symbol-autoload nil) (setq help-window-select t) ;; Eglot (setq eglot-sync-connect 0) (setq eglot-autoshutdown t) (setq eglot-extend-to-xref t) (setq jsonrpc-event-hook nil) (setq eglot-events-buffer-size 0) (setq eglot-events-buffer-config '(:size 0 :format short)) (setq eglot-report-progress nil) ;; Flymake (setq flymake-show-diagnostics-at-end-of-line nil) (setq flymake-wrap-around nil) ;; hl-line-mode (setq hl-line-sticky-flag nil) (setq global-hl-line-sticky-flag nil) ;; icomplete (setq icomplete-compute-delay 0.01) ;; flyspell (setq flyspell-issue-welcome-flag nil) (setq flyspell-issue-message-flag nil) ;; ispell (setq text-mode-ispell-word-completion nil) (setq ispell-silently-savep t) ;; ibuffer (setq ibuffer-formats '((mark modified read-only locked " " (name 55 55 :left :elide) " " (size 8 -1 :right) " " (mode 18 18 :left :elide) " " filename-and-process) (mark " " (name 16 -1) " " filename))) ;; xref (setq xref-show-definitions-function 'xref-show-definitions-completing-read xref-show-xrefs-function 'xref-show-definitions-completing-read) ;; abbrev (setq abbrev-file-name (expand-file-name "abbrev_defs" user-emacs-directory)) (setq save-abbrevs 'silently) ;; dabbrev (setq dabbrev-upcase-means-case-search t) (setq dabbrev-ignored-buffer-modes '(archive-mode image-mode docview-mode tags-table-mode pdf-view-mode)) (setq dabbrev-ignored-buffer-regexps '("\\` " "\\(?:\\(?:[EG]?\\|GR\\)TAGS\\|e?tags\\|GPATH\\)\\(<[0-9]+>\\)?")) ;; Remove warnings from disabled commands (dolist (cmd '(list-timers narrow-to-region upcase-region downcase-region list-threads erase-buffer scroll-left dired-find-alternate-file set-goal-column)) (put cmd 'disabled nil)) ;;; Hooks for built-in modes (add-hook 'elpaca-after-init-hook #'global-auto-revert-mode) (add-hook 'elpaca-after-init-hook #'(lambda () (let ((inhibit-message t)) (recentf-mode 1)))) (add-hook 'kill-emacs-hook #'recentf-cleanup) (add-hook 'elpaca-after-init-hook #'savehist-mode) (add-hook 'elpaca-after-init-hook #'save-place-mode) (setq auth-sources '("~/.authinfo")) ;;; Emacs settings (use-package emacs :ensure nil :custom (delete-selection-mode t) (electric-indent-mode nil) (electric-pair-mode t) (blink-cursor-mode nil) (global-auto-revert-mode t) (indent-tabs-mode nil) (editorconfig-mode t) (recentf-mode t) (recentf-max-saved-items 50) (global-display-line-numbers-mode t) (split-width-threshold nil) :bind (([escape] . keyboard-escape-quit) ("C-x c" . compile))) ;;; Theme (use-package naysayer-theme :ensure nil :config (load-theme 'naysayer t) :load-path rs/lib-dir) (set-face-foreground 'font-lock-builtin-face "lightgreen") ;;; Completion (use-package vertico :ensure t :defer t :commands (vertico-mode vertico-multiform-mode) :custom (vertico-cycle t) (vertico-multiform-commands '((consult-imenu buffer indexed) (find-file flat (vertico-cycle . t)) (execute-extended-command flat (vertico-cycle . t)))) (vertico-multiform-categories '((consult-grep buffer))) :hook (elpaca-after-init . vertico-mode) (elpaca-after-init . vertico-multiform-mode)) (use-package orderless :ensure t :custom (completion-styles '(orderless basic)) (completion-category-defaults nil) (completion-category-overrides '((file (styles partial-completion))))) (use-package marginalia :ensure t :defer t :commands (marginalia-mode marginalia-cycle) :hook (elpaca-after-init . marginalia-mode)) (use-package embark :ensure t :defer t :commands (embark-act embark-dwim embark-export embark-collect embark-bindings embark-prefix-help-command) :bind (("C-." . embark-act) ("C-;" . embark-dwim) ("C-h B" . embark-bindings)) :init (setq prefix-help-command #'embark-prefix-help-command) :config (add-to-list 'display-buffer-alist '("\\`\\*Embark Collect \\(Live\\|Completions\\)\\*" nil (window-parameters (mode-line-format . none))))) (use-package embark-consult :ensure t :hook (embark-collect-mode . consult-preview-at-point-mode)) (use-package consult :ensure t :bind (("C-c M-x" . consult-mode-command) ("C-c h" . consult-history) ("C-c k" . consult-kmacro) ("C-c m" . consult-man) ("C-c i" . consult-info) ([remap Info-search] . consult-info) ("C-x M-:" . consult-complex-command) ("C-x b" . consult-buffer) ("C-x 4 b" . consult-buffer-other-window) ("C-x 5 b" . consult-buffer-other-frame) ("C-x t b" . consult-buffer-other-tab) ("C-x r b" . consult-bookmark) ("C-x p b" . consult-project-buffer) ("M-#" . consult-register-load) ("M-'" . consult-register-store) ("C-M-#" . consult-register) ("M-y" . consult-yank-pop) ("M-g e" . consult-compile-error) ("M-g f" . consult-flymake) ("M-g g" . consult-goto-line) ("M-g M-g" . consult-goto-line) ("M-g o" . consult-outline) ("M-g m" . consult-mark) ("M-g k" . consult-global-mark) ("M-g i" . consult-imenu) ("M-g I" . consult-imenu-multi) ("M-s d" . consult-find) ("M-s c" . consult-locate) ("M-s g" . consult-grep) ("M-s G" . consult-git-grep) ("M-s r" . consult-ripgrep) ("M-s l" . consult-line) ("M-s L" . consult-line-multi) ("M-s k" . consult-keep-lines) ("M-s u" . consult-focus-lines) ("M-s e" . consult-isearch-history) :map isearch-mode-map ("M-e" . consult-isearch-history) ("M-s e" . consult-isearch-history) ("M-s l" . consult-line) ("M-s L" . consult-line-multi) :map minibuffer-local-map ("M-s" . consult-history) ("M-r" . consult-history)) :hook (completion-list-mode . consult-preview-at-point-mode) :init (setq register-preview-delay 0.5 register-preview-function #'consult-register-format) (advice-add #'register-preview :override #'consult-register-window) (setq xref-show-xrefs-function #'consult-xref xref-show-definitions-function #'consult-xref) :config (consult-customize consult-theme :preview-key '(:debounce 0.2 any) consult-ripgrep consult-git-grep consult-grep consult-bookmark consult-recent-file consult-xref consult--source-bookmark consult--source-file-register consult--source-recent-file consult--source-project-recent-file :preview-key '(:debounce 0.4 any)) (setq consult-narrow-key "<")) ;;; Outline Indent (use-package outline-indent :ensure t :defer t :commands outline-indent-minor-mode :init (add-hook 'python-mode-hook #'outline-indent-minor-mode) (add-hook 'python-ts-mode-hook #'outline-indent-minor-mode) (add-hook 'yaml-mode-hook #'outline-indent-minor-mode) (add-hook 'yaml-ts-mode-hook #'outline-indent-minor-mode) :custom (outline-indent-ellipsis " ▼ ")) ;;; Dired (defun rs/dired-copy-path-at-point () (interactive) (dired-copy-filename-as-kill 0)) (use-package dired :ensure nil :bind (("C-x -" . dired-jump) ("C-x e" . dired-jump) (:map dired-mode-map ("-" . dired-up-directory) ("W" . rs/dired-copy-path-at-point))) :custom (dired-deletion-confirmer #'y-or-n-p) (dired-dwim-target t) (dired-listing-switches "-AlhcF") (dired-auto-revert-buffer t)) (use-package dired-x :after dired :ensure nil) (add-hook 'dired-mode-hook #'dired-hide-details-mode) (setq dired-omit-files (concat "\\`[.]\\'" "\\|\\(?:\\.js\\)?\\.meta\\'" "\\|\\.\\(?:elc|a\\|o\\|pyc\\|pyo\\|swp\\|class\\)\\'" "\\|^\\.DS_Store\\'" "\\|^\\.\\(?:svn\\|git\\)\\'" "\\|^\\.ccls-cache\\'" "\\|^__pycache__\\'" "\\|^\\.project\\(?:ile\\)?\\'" "\\|^flycheck_.*" "\\|^flymake_.*")) (add-hook 'dired-mode-hook #'dired-omit-mode) ;;; Undo Tree (use-package undo-tree :ensure t :defer t :diminish :bind (("C-M-u" . undo-tree-visualize) ("M-_" . undo-tree-redo) ("M--" . go-back-to-last-edit) :map undo-tree-map (("C-x u" . nil))) :custom (undo-tree-visualizer-diff t) (undo-tree-enable-undo-in-region t) (undo-tree-history-directory-alist `((".*" . ,(expand-file-name "undo" rs/emacs-dir)))) :config (defun go-back-to-last-edit () "Jump back to the last change in the current buffer." (interactive) (ignore-errors (let ((inhibit-message t)) (undo-tree-undo) (undo-tree-redo)))) (defun undo-tree-split-side-by-side (original-function &rest args) "Split undo-tree side-by-side" (let ((split-height-threshold nil) (split-width-threshold 0)) (apply original-function args))) (advice-add 'undo-tree-visualize :around #'undo-tree-split-side-by-side) :hook (elpaca-after-init . global-undo-tree-mode)) ;;; Multiple Cursors (use-package multiple-cursors :bind (("C-S-c C-S-c" . mc/edit-lines) ("C-M-SPC" . set-rectangular-region-anchor) ("C->" . mc/mark-next-like-this) ("C-<" . mc/mark-previous-like-this) ("C-c C-<" . mc/mark-all-like-this) ("C-\"" . mc/skip-to-next-like-this) ("C-:" . mc/skip-to-previous-like-this))) (use-package move-text :bind (("M-p" . move-text-up) ("M-n" . move-text-down))) ;;; Magit (use-package magit :commands magit-status :bind ("C-x g" . magit)) (use-package forge :after magit) ;;; Programming Languages (use-package nix-mode :mode "\\.nix\\'") (use-package python-mode :mode ("\\.py\\'" . python-mode)) (use-package go-mode :mode ("\\.go\\'" . go-mode)) (use-package csv-mode :mode ("\\.csv\\'" . csv-mode)) (use-package markdown-mode :mode ("\\.md\\'" . markdown-mode)) (use-package lua-mode :mode ("\\.lua\\'" . lua-mode)) (use-package yaml-mode :mode (("\\.yml\\'" . yaml-mode) ("\\.yaml\\'" . yaml-mode))) (use-package yasnippet :commands (yas-minor-modeb yas-reload-all) :config (yas-reload-all) :hook (prog-mode . yas-minor-mode)) (use-package yasnippet-snippets :after yasnippet) (use-package clipetty :hook (elpaca-after-init . global-clipetty-mode)) ;;; Miscellaneous (global-unset-key (kbd "C-M-_")) (use-package diminish) (use-package rainbow-mode :commands (rainbow-mode)) (use-package rainbow-delimiters :hook (prog-mode . rainbow-delimiters-mode)) (use-package term-keys :ensure (:host github :repo "CyberShadow/term-keys") :config (unless (display-graphic-p) (term-keys-mode t))) (use-package windmove :ensure nil :bind (("C-c " . windmove-left) ("C-c " . windmove-right) ("C-c " . windmove-up) ("C-c " . windmove-down))) (use-package ace-window :defer t :commands (ace-window) :bind ("C-x C-o" . ace-window)) (use-package transient) (use-package compile-angel :diminish :ensure t :demand t :config (setq compile-angel-verbose nil) (setq compile-angel-excluded-files-regexps '("/init\\.el$" "/early-init\\.el$")) (compile-angel-on-load-mode) (add-hook 'emacs-lisp-mode-hook #'compile-angel-on-save-local-mode)) (use-package which-key :defer t :commands which-key-mode :hook (elpaca-after-init . which-key-mode) :custom (which-key-idle-delay 1.5) (which-key-idle-secondary-delay 0.25) (which-key-add-column-padding 1) (which-key-max-description-length 40)) (unless (and (eq window-system 'mac) (bound-and-true-p mac-carbon-version-string)) (setq pixel-scroll-precision-use-momentum nil) (pixel-scroll-precision-mode 1)) (unless (display-graphic-p) (xterm-mouse-mode 1) (global-set-key (kbd "") 'scroll-down-line) (global-set-key (kbd "") 'scroll-up-line)) (display-time-mode 1) (show-paren-mode 1) (winner-mode 1) (delete-selection-mode 1) (setq confirm-kill-emacs 'y-or-n-p) (use-package uniquify :ensure nil :custom (uniquify-buffer-name-style 'reverse) (uniquify-separator "•") (uniquify-after-kill-buffer-p t) (uniquify-ignore-buffers-re "^\\*")) (add-hook 'elpaca-after-init-hook #'window-divider-mode) (add-hook 'text-mode-hook #'flyspell-mode) ;;; Window splitting (defun delete-window-evenly () (interactive) (command-execute 'delete-window) (command-execute 'balance-windows)) (global-set-key (kbd "C-x 0") 'delete-window-evenly) (defun split-vertical-evenly () (interactive) (command-execute 'split-window-vertically) (command-execute 'balance-windows)) (global-set-key (kbd "C-x 2") 'split-vertical-evenly) (defun split-horizontal-evenly () (interactive) (command-execute 'split-window-horizontally) (command-execute 'balance-windows)) (global-set-key (kbd "C-x 3") 'split-horizontal-evenly) ;;; Zoom (defun rs/zoom-frame (&optional amt frame) "Increaze FRAME font size by amount AMT. Defaults to selected frame if FRAME is nil, and to 1 if AMT is nil." (interactive "p") (let* ((frame (or frame (selected-frame))) (font (face-attribute 'default :font frame)) (size (font-get font :size)) (amt (or amt 1)) (new-size (+ size amt))) (set-frame-font (font-spec :size new-size) t `(,frame)) (message "Frame's font new size: %d" new-size))) (defun rs/zoom-frame-out (&optional amt frame) "Call `rs/zoom-frame' with negative argument." (interactive "p") (rs/zoom-frame (- (or amt 1)) frame)) (global-set-key (kbd "C-=") 'rs/zoom-frame) (global-set-key (kbd "C--") 'rs/zoom-frame-out) (global-set-key (kbd "") 'rs/zoom-frame) (global-set-key (kbd "") 'rs/zoom-frame-out) (global-set-key (kbd "") 'rs/zoom-frame) (global-set-key (kbd "") 'rs/zoom-frame-out) ;;; Org (setq org-directory rs/notes-dir) (setq org-agenda-files '("agenda.org")) (setq org-tag-alist '((:startgroup) ("home" . ?h) ("work" . ?w) ("school" . ?s) (:endgroup) (:newline) (:startgroup) ("one-shot" . ?o) ("project" . ?j) ("tiny" . ?t) (:endgroup) ("meta") ("review") ("reading"))) (setq org-refile-targets '(("agenda.org" :maxlevel . 2) ("archive.org" :level . 1))) (setq org-archive-location "archive.org::* Archived") (setq org-link-abbrev-alist '(("family_search" . "https://www.familysearch.org/tree/person/details/%s"))) (use-package org :ensure nil :hook ((org-mode . visual-line-mode) (org-mode . flyspell-mode)) :init (with-eval-after-load 'org (require 'mu4e-org nil t)) :bind (:map global-map ("C-c a" . org-agenda) ("C-c c" . org-capture) ("C-c l s" . org-store-link) ("C-c l i" . org-insert-link-global)) :custom (org-time-stamp-rounding-minutes '(0 5)) (org-read-date-prefer-future t) (org-return-follows-link t) (org-agenda-window-setup 'current-window) (org-agenda-start-with-log-mode t) (org-agenda-include-diary nil) (org-agenda-tags-column -80) (org-agenda-block-separator ?─) (org-agenda-time-grid '((daily today require-timed) (800 1000 1200 1400 1600 1800 2000) " ┄┄┄┄┄ " "┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄")) (org-agenda-current-time-string "◀── now ──────────────────────────────────────") :config (require 'oc-csl) (add-to-list 'org-export-backends 'md) (setf (cdr (assoc 'file org-link-frame-setup)) 'find-file) (setq org-export-with-smart-quotes t) (setq org-todo-keywords '((sequence "TODO(t)" "WAITING(w@/!)" "STARTED(s!)" "|" "DONE(d!)" "OBSOLETE(o@)"))) (setq org-outline-path-complete-in-steps nil) (setq org-refile-use-outline-path 'file) (setq org-capture-templates '(("c" "Quick Capture" entry (file+headline "agenda.org" "Inbox") "** TODO %?\n%U\n%i") ("r" "Capture with Reference" entry (file+headline "agenda.org" "Inbox") "** TODO %?\n%U\n%i\n%a") ("t" "Task" entry (file+headline "agenda.org" "Tasks") "** TODO %?\n%U\n%i") ("m" "Meeting" entry (file+headline "agenda.org" "Meetings") "** TODO %?\n%U\n%i\n%a") ("e" "Email") ("ei" "Email to Inbox" entry (file+headline "agenda.org" "Inbox") "** TODO %:subject\nFrom: %:fromname <%:fromaddress>\nEmail: %a\n%?") ("et" "Email to Tasks" entry (file+headline "agenda.org" "Tasks") "** TODO %:subject\nFrom: %:fromname <%:fromaddress>\nEmail: %a\n%?") ("ef" "Email Follow-up" entry (file+headline "agenda.org" "Tasks") "** TODO Follow up: %:subject\nDEADLINE: %(org-insert-time-stamp (org-read-date nil t \"+2d\"))\nFrom: %:fromname\nEmail: %a\n%?"))) (setq org-agenda-custom-commands '(("n" "Dashboard" ((tags-todo "+DEADLINE<\"\"" ((org-agenda-overriding-header "⚠ Overdue!"))) (agenda "" ((org-agenda-span 'day) (org-deadline-warning-days 7) (org-agenda-time-grid nil) (org-agenda-overriding-header "📅 Today"))) (todo "STARTED" ((org-agenda-overriding-header "🔨 In Progress"))) (todo "WAITING" ((org-agenda-overriding-header "⏳ Waiting"))) (tags-todo "+DEADLINE>=\"\"+DEADLINE<=\"<+7d>\"" ((org-agenda-overriding-header "📆 Due This Week"))) (tags-todo "inbox" ((org-agenda-overriding-header "📥 Inbox"))) (agenda "" ((org-agenda-span 7) (org-agenda-start-day "+1d") (org-agenda-start-on-weekday nil) (org-agenda-show-all-dates t) (org-agenda-time-grid nil) (org-agenda-overriding-header "📅 Next 7 Days"))) (alltodo "" ((org-agenda-skip-function '(org-agenda-skip-entry-if 'scheduled 'deadline 'todo '("STARTED" "WAITING"))) (org-agenda-overriding-header "📋 Backlog"))))) ("w" "Week View" ((agenda "" ((org-agenda-span 'week) (org-deadline-warning-days 14))))) ("i" "Inbox" tags-todo "inbox") ("p" "Projects" tags-todo "project")))) ;;; Denote (use-package denote :ensure t :custom (denote-directory rs/notes-dir) (denote-file-type 'markdown-yaml) (denote-known-keywords '("project" "idea" "ref" "meeting" "paper" "misc")) (denote-prompts '(subdirectory title keywords)) (denote-excluded-directories-regexp "^\\.\\|^scripts$") :bind (("C-c n n" . denote) ("C-c n f" . denote-open-or-create) ("C-c n d" . denote-sort-dired) ("C-c n i" . denote-link) ("C-c n l" . denote-backlinks) ("C-c n r" . denote-rename-file) ("C-c n R" . denote-rename-file-using-front-matter) ("C-c n k" . denote-keywords-add) ("C-c n K" . denote-keywords-remove)) :config (denote-rename-buffer-mode 1)) (use-package denote-org :ensure t :commands (denote-org-link-to-heading denote-org-backlinks-for-heading denote-org-extract-org-subtree denote-org-convert-links-to-file-type denote-org-convert-links-to-denote-type denote-org-dblock-insert-files denote-org-dblock-insert-links denote-org-dblock-insert-backlinks denote-org-dblock-insert-missing-links denote-org-dblock-insert-files-as-headings)) (use-package denote-org-extras :ensure nil :after denote :config (denote-org-extras-dblock-backlinks-mode 1) :bind (("C-c n b" . denote-org-extras-backlinks-for-heading) ("C-c n h" . denote-org-extras-link-to-heading))) (use-package denote-journal :ensure t :custom (denote-journal-directory "journal/") (denote-journal-keyword "journal") (denote-journal-title-format 'day-date-month-year) :bind (("C-c n j" . denote-journal-new-entry) ("C-c n J" . denote-journal-new-or-existing-entry))) (use-package denote-sequence :ensure t :bind (("C-c n s s" . denote-sequence) ("C-c n s c" . denote-sequence-new-child-of-current) ("C-c n s S" . denote-sequence-new-sibling-of-current) ("C-c n s f" . denote-sequence-find) ("C-c n s l" . denote-sequence-link) ("C-c n s d" . denote-sequence-dired))) (use-package denote-explore :ensure t :bind (("C-c n e n" . denote-explore-count-notes) ("C-c n e k" . denote-explore-count-keywords) ("C-c n e r" . denote-explore-random-note) ("C-c n e l" . denote-explore-random-link) ("C-c n e g" . denote-explore-network) ("C-c n e b" . denote-explore-barchart-keywords) ("C-c n e s" . denote-explore-sync-metadata))) ;;; Email (mu4e) (use-package mu4e :ensure (:host github :repo "djcb/mu" :files ("mu4e/*.el") :pre-build (with-temp-file "mu4e/mu4e-config.el" (insert (format "(defconst mu4e-mu-version %S)\n" (string-trim (shell-command-to-string "mu --version | head -1 | awk '{print $2}'"))) "(provide 'mu4e-config)\n"))) :defer t :commands (mu4e mu4e-compose-new) :bind ("C-c e" . mu4e) :config (setq mu4e-mu-binary (or (executable-find "mu") rs/mu-binary)) (require 'mu4e-org) (setq mu4e-maildir rs/mail-dir) (setq mu4e-get-mail-command rs/mbsync-command) (setq mu4e-update-interval 180) (setq mu4e-change-filenames-when-moving t) (setq sendmail-program "msmtp") (setq send-mail-function 'sendmail-send-it) (setq message-sendmail-f-is-evil t) (setq message-sendmail-extra-arguments '("--read-envelope-from")) (setq message-send-mail-function 'message-send-mail-with-sendmail) (setq message-kill-buffer-on-exit t) (setq mu4e-headers-date-format "%Y-%m-%d") (setq mu4e-headers-time-format "%H:%M") (setq mu4e-view-show-addresses t) (setq mu4e-view-show-images t) (setq mu4e-compose-dont-reply-to-self t) (setq mm-discouraged-alternatives '("text/html" "text/richtext")) (setq mml-secure-openpgp-sign-with-sender t) (setq mm-sign-option nil) (defun mu4e-compose-sign-message () "Sign the current message with GPG." (interactive) (mml-secure-message-sign-pgpmime)) (add-hook 'mu4e-compose-mode-hook (lambda () (local-set-key (kbd "C-c C-s") 'mu4e-compose-sign-message))) (add-hook 'mu4e-headers-mode-hook (lambda () (local-set-key (kbd "r") 'mu4e-compose-reply) (local-set-key (kbd "R") 'mu4e-compose-wide-reply))) (add-hook 'mu4e-view-mode-hook (lambda () (local-set-key (kbd "r") 'mu4e-compose-reply) (local-set-key (kbd "R") 'mu4e-compose-wide-reply))) (define-key mu4e-headers-mode-map (kbd "e") 'mu4e-headers-mark-for-refile) (define-key mu4e-view-mode-map (kbd "e") 'mu4e-view-mark-for-refile) (define-key mu4e-headers-mode-map (kbd "t") 'mu4e-headers-mark-for-something) (defun mu4e-headers-mark-all-for-something () "Mark all messages in current view for an action (prompts once)." (interactive) (let* ((mark (mu4e-read-option "Mark all as: " '(("Read" . read) ("Unread" . unread) ("Trash" . trash) ("Delete" . delete) ("Archive" . refile) ("Move" . move) ("Flag" . flag) ("uNflag" . unflag)))) (target (when (eq mark 'move) (mu4e-ask-maildir "Move all to: "))) (count 0)) (save-excursion (goto-char (point-min)) (while (and (not (eobp)) (not (get-text-property (point) 'msg))) (forward-line 1)) (while (not (eobp)) (when (get-text-property (point) 'msg) (cond ((eq mark 'move) (mu4e-mark-at-point 'move target)) ((eq mark 'refile) (mu4e-mark-at-point 'refile (mu4e--mark-get-refile-target (mu4e-message-at-point)))) (t (mu4e-mark-at-point mark nil))) (setq count (1+ count))) (forward-line 1))) (message "Marked %d messages for %s" count mark))) (define-key mu4e-headers-mode-map (kbd "T") 'mu4e-headers-mark-all-for-something) (setq mu4e-completing-read-function 'completing-read) (setq mu4e-contexts `(,(make-mu4e-context :name "uchicago" :match-func (lambda (msg) (when msg (string-prefix-p "/uchicago" (mu4e-message-field msg :maildir)))) :vars `((user-mail-address . ,rs/email-uchicago) (user-full-name . ,rs/full-name) (mu4e-drafts-folder . "/uchicago/Drafts") (mu4e-sent-folder . "/uchicago/Sent") (mu4e-trash-folder . "/uchicago/Trash") (mu4e-refile-folder . "/uchicago/Archive") (message-sendmail-extra-arguments . ("--read-envelope-from" "-a" "uchicago")))) ,(make-mu4e-context :name "personal" :match-func (lambda (msg) (when msg (string-prefix-p "/personal" (mu4e-message-field msg :maildir)))) :vars `((user-mail-address . ,rs/email-personal) (user-full-name . ,rs/full-name) (mu4e-drafts-folder . "/personal/[Gmail]/Drafts") (mu4e-sent-folder . "/personal/[Gmail]/Sent Mail") (mu4e-trash-folder . "/personal/[Gmail]/Trash") (mu4e-refile-folder . "/personal/Archive") (message-sendmail-extra-arguments . ("--read-envelope-from" "-a" "personal")))))) (setq mu4e-context-policy 'pick-first) (setq mu4e-compose-context-policy 'ask-if-none) (setq mu4e-bookmarks '((:name "Unread messages" :query "flag:unread AND NOT flag:trashed" :key ?u) (:name "Today's messages" :query "date:today..now" :key ?t) (:name "Last 7 days" :query "date:7d..now" :key ?w) (:name "UChicago Inbox" :query "maildir:/uchicago/Inbox" :key ?i) (:name "Personal Inbox" :query "maildir:/personal/Inbox" :key ?p)))) (use-package wakatime-mode :config (global-wakatime-mode)) ;;; init.el ends here