nix/config/emacs/init.el

1087 lines
36 KiB
EmacsLisp

;;; 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 <left>" . windmove-left)
("C-c <right>" . windmove-right)
("C-c <up>" . windmove-up)
("C-c <down>" . 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 "<mouse-4>") 'scroll-down-line)
(global-set-key (kbd "<mouse-5>") '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 "<C-down-mouse-4>") 'rs/zoom-frame)
(global-set-key (kbd "<C-down-mouse-5>") 'rs/zoom-frame-out)
(global-set-key (kbd "<C-wheel-up>") 'rs/zoom-frame)
(global-set-key (kbd "<C-wheel-down>") '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<\"<today>\""
((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>=\"<today>\"+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