;-*- eval: (load-file "./init-dev.el"); -*- #+title: My Emacs Configuration #+options: ^:{} html-postamble:nil #+property: header-args :mkdirp yes :tangle yes :tangle-mode: #o444 :results silent :noweb yes #+archive: archives/%s::datetree/ #+startup: indent * An Explanation This is a literate configuration for =Emacs=. Tangling this file creates an Elisp file, =~/.emacs.d/lisp/init.el=. * Bootstrapping ** early-init :PROPERTIES: :header-args: :tangle-mode o444 :results silent :tangle ~/.emacs.d/early-init.el :END: Emacs 27.0 introduced an early-init file. It allows customization before package and UI initialization. #+begin_src emacs-lisp :lexical t ;;; early-init.el --- Emacs pre package.el & GUI configuration -*- lexical-binding: t; -*- ;;; Code: #+end_src #+begin_src emacs-lisp :lexical t (setq package-enable-at-startup nil) (setq inhibit-default-init nil) #+end_src #+begin_src emacs-lisp :lexical t (setq native-comp-async-report-warnings-errors nil) #+end_src *** Debugging Running this form will launch the debugger after loading a package. This is useful for finding out when a dependency is requiring a package (perhaps earlier than you want). Use by tangling this block and launching Emacs with =emacs --debug-init=. #+begin_src emacs-lisp :var file="" :results silent :tangle no (unless (string-empty-p file) (eval-after-load file '(debug))) #+end_src Similarly, this variable will hit the debugger when a message matches its regexp. #+begin_src emacs-lisp :tangle no (setq debug-on-message "") #+end_src Adding a variable watcher can be a useful way to track down initialization and mutation of a variable. #+begin_src emacs-lisp :tangle no (add-variable-watcher 'org-capture-after-finalize-hook (lambda (symbol newval operation where) (debug) (message "%s set to %s" symbol newval))) #+end_src #+begin_src emacs-lisp :tangle no (setq debug-on-error t) #+end_src *** file-name-handler-alist Skipping a bunch of regular expression searching in the =file-name-handler-alist= should improve start time. #+begin_src emacs-lisp :lexical t (defvar default-file-name-handler-alist file-name-handler-alist) (setq file-name-handler-alist nil) #+end_src *** Garbage Collection =gc-cons-threshold= (800 KB) and =gc-cons-percentage= (0.1) control when the Emacs garbage collector can kick in. Temporarily turning these off during init should decrease startup time. Resetting them afterward will ensure that normal operations don't suffer from a large GC periods. The following is a table shows values from popular Emacs configurations. | Distribution | gc-cons-threshold | |--------------+-------------------| | Default | 800000 | | Doom | 16777216 | | Spacemacs | 100000000 | #+begin_src emacs-lisp :lexical t (setq gc-cons-threshold most-positive-fixnum gc-cons-percentage 1) (defun +gc-after-focus-change () "Run GC when frame loses focus." (run-with-idle-timer 5 nil (lambda () (unless (frame-focus-state) (garbage-collect))))) #+end_src #+begin_src emacs-lisp :lexical t (defun +reset-init-values () (run-with-idle-timer 1 nil (lambda () (setq file-name-handler-alist default-file-name-handler-alist gc-cons-percentage 0.1 gc-cons-threshold 100000000) (message "gc-cons-threshold & file-name-handler-alist restored") (when (boundp 'after-focus-change-function) (add-function :after after-focus-change-function #'+gc-after-focus-change))))) (with-eval-after-load 'elpaca (add-hook 'elpaca-after-init-hook '+reset-init-values)) #+end_src *** UI Turning off these visual elements before UI initialization should speed up init. #+begin_src emacs-lisp :lexical t (push '(menu-bar-lines . 0) default-frame-alist) (push '(tool-bar-lines . 0) default-frame-alist) (push '(vertical-scroll-bars) default-frame-alist) #+end_src Prevent instructions on how to close an emacsclient frame. #+begin_src emacs-lisp :lexical t (setq server-client-instructions nil) #+end_src Implicitly resizing the Emacs frame adds to init time. Fonts larger than the system default can cause frame resizing, which adds to startup time. #+begin_src emacs-lisp :lexical t (setq frame-inhibit-implied-resize t) #+end_src Set default and backup fonts #+begin_src emacs-lisp :lexical t (push '(font . "Source Code Pro") default-frame-alist) (set-face-font 'default "Source Code Pro") (set-face-font 'variable-pitch "DejaVu Sans") (copy-face 'default 'fixed-pitch) #+end_src Ignore X resources. #+begin_src emacs-lisp :lexical t (advice-add #'x-apply-session-resources :override #'ignore) #+end_src Taken from: [[https://github.com/vsemyonoff/emacsrc/blob/14649a5bafea99cc7e13e7d048e9d15aed7926ce/early-init.el]] This helps with a bug I was hitting when using =desktop-save-mode='s =desktop-read=. #+begin_src emacs-lisp :lexical t (setq desktop-restore-forces-onscreen nil) #+end_src Silence the bell function #+begin_src emacs-lisp :lexical t (setq ring-bell-function #'ignore inhibit-startup-screen t) #+end_src *** provide early-init #+begin_src emacs-lisp :lexical t (provide 'early-init) ;;; early-init.el ends here #+end_src ** lexical binding The following line turns on lexical binding for performance reasons. #+begin_src emacs-lisp :lexical t ;; -*- lexical-binding: t; -*- #+end_src ** profiling This function displays how long Emacs took to start. It's a rough way of knowing when/if I need to optimize my init file. #+begin_src emacs-lisp :lexical t (add-hook 'elpaca-after-init-hook (lambda () (message "Emacs loaded in %s with %d garbage collections." (format "%.2f seconds" (float-time (time-subtract (current-time) before-init-time))) gcs-done))) #+end_src We can also enable the profiler to view a report after init. #+begin_src emacs-lisp :lexical t :tangle no (profiler-start 'cpu+mem) (add-hook 'elpaca-after-init-hook (lambda () (profiler-stop) (profiler-report))) #+end_src ELP is useful for seeing which functions in a package are "hot". #+begin_src emacs-lisp :var file="elpaca" :lexical t :tangle no (require 'elp) (with-eval-after-load file (elp-instrument-package file)) (add-hook 'elpaca-after-init-hook (lambda () (elp-results) (elp-restore-package (intern file)))) #+end_src ** initial-buffer-choice #+begin_src emacs-lisp :lexical t (setq initial-buffer-choice t) ;;*scratch* #+end_src ** theme I prefer to keep my themes in a sub-folder of =~/.emacs.d= #+begin_src emacs-lisp :lexical t (setq custom-theme-directory "~/.emacs.d/themes/") #+end_src I'm working on a theme that is readable and attractive. #+begin_src emacs-lisp :lexical t (defvar +theme 'mine "Default theme.") (require 'cl-lib) (require 'custom) ;; remove synthetic use-package theme (unless (remq 'use-package custom-enabled-themes) (load-theme +theme t)) #+end_src ** Elpaca #+begin_quote An elisp package manager https://github.com/progfolio/elpaca #+end_quote *** Installer #+begin_src emacs-lisp :lexical t (defvar elpaca-installer-version 0.7) (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 nil :pre-build ("git" "remote" "set-url" "origin" "git@github.com:progfolio/elpaca.git") :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) (condition-case-unless-debug err (if-let ((buffer (pop-to-buffer-same-window "*elpaca-bootstrap*")) ((zerop (call-process "git" nil buffer t "clone" (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))) (and (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 :depth nil)) (setq elpaca-queue-limit 30) #+end_src #+begin_src emacs-lisp :lexical t (with-eval-after-load 'evil (evil-make-intercept-map elpaca-ui-mode-map)) #+end_src ** EXWM #+begin_src emacs-lisp :lexical t (when (getenv "EXWM") (load-theme 'modus-vivendi t) (elpaca exwm (require 'exwm) (defun +exwm-edit () "Edit EXWM input field." (interactive) (require 'string-edit) (unless (derived-mode-p 'exwm-mode) (user-error "Buffer not in EXWM mode.")) (with-current-buffer (if (minibufferp) (window-buffer (minibuffer-selected-window)) (current-buffer)) (let ((xwin-id (exwm--buffer->id (current-buffer))) (previous (gui-get-selection 'CLIPBOARD 'UTF8_STRING)) (selection (progn ;; Select current text input. (exwm-input--fake-key ?\C-a) (sit-for 0.2) ;;@HACK: Wait for Xserver (gui-get-selection 'PRIMARY 'UTF8_STRING))) (b (current-buffer)) (string-edit-mode-hook #'flyspell-mode)) (string-edit (or exwm-title "*EXWM-edit*") (or selection "") (lambda (s) (if (not (buffer-live-p b)) (user-error "Target buffer not live") (with-current-buffer b (gui-set-selection 'CLIPBOARD s) (sit-for 0.2) (exwm-input--fake-key ?\C-v)))) :abort-callback (lambda () (when (buffer-live-p b) (with-current-buffer b (gui-set-selection 'CLIPBOARD previous)))))))) (defun +exwm-launch (command) (interactive (list (progn (exwm-layout-unset-fullscreen) (read-shell-command "$ ")))) (start-process-shell-command command nil command) ) (defun +exwm-shell-command (var) "Execute environmental VAR as async shell command." (interactive) (if-let ((program (getenv var))) (progn (start-process-shell-command program nil program)) (user-error "$%S not set" var))) (defun +exwm-move-window-right () "Move current window right like i3wm does." (interactive) (condition-case _ (windmove-swap-states-right) ((user-error) (let ((split (split-root-window-right))) (delete-window) (select-window split))))) (defun +exwm-move-window-left () "Move current window right like i3wm does." (interactive) (condition-case _ (windmove-swap-states-left) ((user-error) (let ((split (split-root-window-right))) (delete-window) (select-window split))))) (defun +exwm-move-window-up () "Move current window right like i3wm does." (interactive) (condition-case _ (windmove-swap-states-up) ((user-error) (let ((split (split-root-window-below))) (delete-window) (select-window split))))) (defun +exwm-move-window-down () "Move current window right like i3wm does." (interactive) (condition-case _ (windmove-swap-states-down) ((user-error) (let ((split (split-root-window-below))) (delete-window) (select-window split))))) (defun +exwm-toggle-fullscreen () "Toggle fullscreen in all windows." (interactive) (if (derived-mode-p 'exwm-mode) (call-interactively #'exwm-layout-toggle-fullscreen) (+toggle-maximize-buffer))) (defvar +exwm-split-side 'right) (setopt exwm-input-global-keys `(([?\s-l] . windmove-right) ([?\M-\s-l] . +exwm-move-window-right) ([?\s-h] . windmove-left) ([?\M-\s-h] . +exwm-move-window-left) ([?\s-j] . windmove-down) ([?\M-\s-j] . +exwm-move-window-down) ([?\s-k] . windmove-up) ([?\M-\s-k] . +exwm-move-window-up) ([?\s-i] . +exwm-edit) ([?\s-r] . exwm-restart) ([?\s-f] . +exwm-toggle-fullscreen) ([?\s-m] . exwm-layout-toggle-mode-line) ([?\s-n] . tab-next) ([?\s-p] . tab-previous) ([?\s-t] . tab-bar-switch-to-tab) ([s-return] . (lambda () (interactive) (+exwm-shell-command "TERMINAL"))) ([?\s-b] . (lambda () (interactive) (+exwm-shell-command "BROWSER"))) ([?\s-?] . (lambda () (interactive) (setq +exwm-split-side nil))) ([?\s-/] . (lambda () (interactive) (setq +exwm-split-side 'right))) ([?\s-d] . (lambda () (interactive) (kill-this-buffer) (ignore-errors (delete-window)) (balance-windows))) ([?\s-\;] . +exwm-launch) ([?\s-\ ] . execute-extended-command) (,(kbd "") . (lambda () (interactive) (make-process :name "emacs" :command '("emacs")))))) (defun +exwm-rename-workspace-buffer () "Rename workspace buffer." (exwm-workspace-rename-buffer (concat (when exwm-title (if (> (length exwm-title) (/ (window-width) 3.0)) (concat (substring exwm-title 0 (/ (length exwm-title) 2)) "...") exwm-title)) (when exwm-title " | ") exwm-class-name))) (add-hook 'exwm-update-class-hook #'+exwm-rename-workspace-buffer) (add-hook 'exwm-update-title-hook #'+exwm-rename-workspace-buffer) ;; (defun +exwm-new-window () ;; ;;(exwm-layout-hide-mode-line) ;; ;; (split-window nil nil +exwm-split-side) ;; ;; (balance-windows)) ;; (set-window-dedicated-p (selected-window) t)) ;; (add-hook 'exwm-manage-finish-hook #'+exwm-new-window) (setq exwm-manage-configurations '(((string-match-p "Void.*Stranger" exwm-title) managed t) (t char-mode t))) (require 'exwm-randr) (exwm-randr-enable) (exwm-enable) ;; So child processes don't inherit the variable (setenv "EXWM")) (with-eval-after-load 'general (exwm-input-set-key [?\s-\ ] #'+prefix-map)) (display-battery-mode) (make-process :name "picom" :command '("picom") :noquery t) (make-process :name "syndaemon" :command '("syndaemon" "-K" "-i" "0.25" "-R" "-d") :noquery t) (elpaca desktop-environment ;; Remove conflicting binding (require 'desktop-environment) (setf (alist-get (aref (kbd "s-l") 0) desktop-environment-mode-map nil 'remove) nil) (desktop-environment-mode))) #+end_src ** packaging *** keychain [[https://www.funtoo.org/Keychain][Keychain]] is a gpg/ssh agent that allows me to cache my credentials. This package gets the correct environment variables so elpaca can use the ssh protocol. #+begin_src emacs-lisp :lexical t ;; We need this loaded for SSH protocol (elpaca-queue (elpaca keychain-environment (require 'keychain-environment) (keychain-refresh-environment))) #+end_src *** melpulls An Elpaca menu for MELPA submissions. #+begin_src emacs-lisp :lexical t (elpaca-queue (elpaca (melpulls :host github :repo "progfolio/melpulls" :protocol ssh) (add-to-list 'elpaca-menu-functions 'melpulls))) #+end_src *** use-package #+begin_src emacs-lisp :lexical t (defmacro use-feature (name &rest args) "Like `use-package' but accounting for asynchronous installation. NAME and ARGS are in `use-package'." (declare (indent defun)) `(use-package ,name :ensure nil ,@args)) #+end_src #+begin_src emacs-lisp :lexical t (elpaca elpaca-use-package (require 'elpaca-use-package) (elpaca-use-package-mode) (setq use-package-always-ensure t)) #+end_src #+begin_src emacs-lisp :lexical t (if debug-on-error (setq use-package-verbose t use-package-expand-minimally nil use-package-compute-statistics t) (setq use-package-verbose nil use-package-expand-minimally t)) #+end_src *** Local Packages that I'm developing or aren't part of any online repositories go in =~/emacs.d/lisp/=. #+begin_src emacs-lisp :lexical t (let ((default-directory "~/.emacs.d/lisp/")) (when (file-exists-p default-directory) (normal-top-level-add-to-load-path '(".")) (normal-top-level-add-subdirs-to-load-path))) #+end_src * Custom variables ** files/paths #+begin_src emacs-lisp :lexical t (setq literate-file (concat user-emacs-directory "init.org")) #+end_src ** terminal utf-8 #+begin_src emacs-lisp :lexical t (defun +terminal () "Set the terimnal coding system." (unless (display-graphic-p) (set-terminal-coding-system 'utf-8))) (add-hook 'server-after-make-frame-hook #'+terminal) #+end_src * Secrets I keep my sensitive, personal information in a separate file so I can publish this configuration publicly. #+begin_src emacs-lisp :lexical t (load "~/Documents/emacs-secrets.el" nil 'nomessage) #+end_src * Packages :PROPERTIES: :VISIBILITY: children :ID: f8affafe-3a4c-490c-a066-006aeb76f628 :CUSTOM_ID: init-packages :END: ** general (key-bindings) :PROPERTIES: :CUSTOM_ID: key-bindings :END: #+begin_quote general.el provides a more convenient method for binding keys in emacs (for both evil and non-evil users). https://github.com/noctuid/general.el#about #+end_quote Load general before the remaining packages so they can make use of the ~:general~ keyword in their declarations. #+begin_src emacs-lisp :lexical t (use-package general :ensure (:wait t) :demand t :config (general-override-mode) (general-auto-unbind-keys) <>) #+end_src *** config :PROPERTIES: :header-args: :noweb-ref general-config :END: The global definer allows me to use a leader key in most states. #+begin_src emacs-lisp :lexical t (general-define-key :keymaps 'override :states '(insert normal hybrid motion visual operator emacs) :prefix-map '+prefix-map :prefix-command '+prefix-map :prefix "SPC" :global-prefix "S-SPC") (general-create-definer global-definer :wk-full-keys nil :keymaps '+prefix-map) #+end_src #+begin_src emacs-lisp :lexical t (global-definer "SPC" 'execute-extended-command "/" 'occur "!" 'shell-command ":" 'eval-expression "." 'repeat "h" (general-simulate-key "C-h" :which-key "help") "z" '((lambda (local) (interactive "p") (unless repeat-mode (repeat-mode)) (let ((local current-prefix-arg) (current-prefix-arg nil)) (call-interactively (if local #'text-scale-adjust #'global-text-scale-adjust)))) :which-key "zoom")) #+end_src We define a global-leader definer to access major-mode specific bindings: #+begin_src emacs-lisp :lexical t (general-create-definer global-leader :keymaps 'override :states '(insert normal hybrid motion visual operator) :prefix "SPC m" :non-normal-prefix "S-SPC m" "" '( :ignore t :which-key (lambda (arg) (cons (cadr (split-string (car arg) " ")) (replace-regexp-in-string "-mode$" "" (symbol-name major-mode)))))) #+end_src And a macro to ease the creation of nested menu bindings: #+begin_src emacs-lisp :lexical t (defmacro +general-global-menu! (name prefix-key &rest body) "Create a definer named +general-global-NAME wrapping global-definer. Create prefix map: +general-global-NAME-map. Prefix bindings in BODY with PREFIX-KEY." (declare (indent 2)) (let* ((n (concat "+general-global-" name)) (prefix-map (intern (concat n "-map")))) `(progn (general-create-definer ,(intern n) :wrapping global-definer :prefix-map (quote ,prefix-map) :prefix ,prefix-key :wk-full-keys nil "" '(:ignore t :which-key ,name)) (,(intern n) ,@body)))) #+end_src **** applications #+begin_src emacs-lisp :lexical t (+general-global-menu! "application" "a" "p" '(:ignore t "elpaca") "pb" 'elpaca-browse "pr" '((lambda () (interactive) (let ((current-prefix-arg (not current-prefix-arg)) (this-command 'elpaca-rebuild)) (call-interactively #'elpaca-rebuild))) :which-key "rebuild") "pm" 'elpaca-manager "pl" 'elpaca-log "pi" 'elpaca-info "pI" '((lambda () (interactive) (info "Elpaca")) :which-key "elpaca-info") "ps" 'elpaca-status "pt" 'elpaca-try "pv" 'elpaca-visit) #+end_src **** buffers #+begin_src emacs-lisp :lexical t (+general-global-menu! "buffer" "b" "d" 'kill-current-buffer "o" '((lambda () (interactive) (switch-to-buffer nil)) :which-key "other-buffer") "p" 'previous-buffer "r" 'rename-buffer "R" 'revert-buffer "M" '((lambda () (interactive) (switch-to-buffer "*Messages*")) :which-key "messages-buffer") "n" 'next-buffer "s" 'scratch-buffer "TAB" '((lambda () (interactive) (switch-to-buffer nil)) :which-key "other-buffer")) #+end_src **** bookmarks #+begin_src emacs-lisp :lexical t (+general-global-menu! "bookmark" "B") #+end_src **** eval #+begin_src emacs-lisp :lexical t (+general-global-menu! "eval" "e" "b" 'eval-buffer "d" 'eval-defun "e" 'eval-expression "p" 'pp-eval-last-sexp "s" 'eval-last-sexp) #+end_src **** files #+begin_src emacs-lisp :lexical t (+general-global-menu! "file" "f" "d" '((lambda (&optional arg) (interactive "P") (let ((buffer (when arg (current-buffer)))) (diff-buffer-with-file buffer))) :which-key "diff-with-file") "e" '(:ignore t :which-key "edit") "ed" '((lambda () (interactive) (find-file-existing literate-file) (widen)) :which-key "dotfile") "f" 'find-file "l" '((lambda (&optional arg) (interactive "P") (call-interactively (if arg #'find-library-other-window #'find-library))) :which-key "+find-library") "p" 'find-function-at-point "P" 'find-function "R" 'rename-file-and-buffer "s" 'save-buffer "v" 'find-variable-at-point "V" 'find-variable) #+end_src **** frames #+begin_src emacs-lisp :lexical t (+general-global-menu! "frame" "F" "D" 'delete-other-frames "F" 'select-frame-by-name "O" 'other-frame-prefix "c" '(:ingore t :which-key "color") "cb" 'set-background-color "cc" 'set-cursor-color "cf" 'set-foreground-color "f" 'set-frame-font "m" 'make-frame-on-monitor "n" 'next-window-any-frame "o" 'other-frame "p" 'previous-window-any-frame "r" 'set-frame-name) #+end_src **** git version-control #+begin_src emacs-lisp :lexical t (+general-global-menu! "git/version-control" "g") #+end_src **** links #+begin_src emacs-lisp :lexical t (+general-global-menu! "link" "l") #+end_src **** narrowing #+begin_src emacs-lisp :lexical t (+general-global-menu! "narrow" "n" "d" 'narrow-to-defun "p" 'narrow-to-page "r" 'narrow-to-region "w" 'widen) #+end_src **** projects #+begin_src emacs-lisp :lexical t (+general-global-menu! "project" "p" "b" '(:ignore t :which-key "buffer")) #+end_src **** quit #+begin_src emacs-lisp :lexical t (+general-global-menu! "quit" "q" "q" 'save-buffers-kill-emacs "r" 'restart-emacs "Q" 'kill-emacs) #+end_src **** spelling #+begin_src emacs-lisp :lexical t (+general-global-menu! "spelling" "s") #+end_src **** text #+begin_src emacs-lisp :lexical t (+general-global-menu! "text" "x" "i" 'insert-char "I" (general-simulate-key "C-x 8" :which-key "iso")) #+end_src **** tabs #+begin_src emacs-lisp :lexical t (+general-global-menu! "tab" "t") #+end_src **** toggle #+begin_src emacs-lisp :lexical t (+general-global-menu! "toggle" "T" "d" '(:ignore t :which-key "debug") "de" 'toggle-debug-on-error "dq" 'toggle-debug-on-quit "s" '(:ignore t :which-key "spelling")) #+end_src **** windows #+begin_src emacs-lisp :lexical t (+general-global-menu! "window" "w" "?" 'split-window-vertically "=" 'balance-windows "/" 'split-window-horizontally "O" 'delete-other-windows "X" '((lambda () (interactive) (call-interactively #'other-window) (kill-buffer-and-window)) :which-key "kill-other-buffer-and-window") "d" 'delete-window "h" 'windmove-left "j" 'windmove-down "k" 'windmove-up "l" 'windmove-right "o" 'other-window "t" 'window-toggle-side-windows "." '(:ingore :which-key "resize") ".h" '((lambda () (interactive) (call-interactively (if (window-prev-sibling) #'enlarge-window-horizontally #'shrink-window-horizontally))) :which-key "divider left") ".l" '((lambda () (interactive) (call-interactively (if (window-next-sibling) #'enlarge-window-horizontally #'shrink-window-horizontally))) :which-key "divider right") ".j" '((lambda () (interactive) (call-interactively (if (window-next-sibling) #'enlarge-window #'shrink-window))) :which-key "divider up") ".k" '((lambda () (interactive) (call-interactively (if (window-prev-sibling) #'enlarge-window #'shrink-window))) :which-key "divider down") "x" 'kill-buffer-and-window) #+end_src **** vim completion #+begin_src emacs-lisp :lexical t ;;vim-like completion (general-create-definer completion-def :prefix "C-x") #+end_src *** TODO org-mode meta-mappings á la Spacemacs. C - F 'clever-insert' in Spacemacs source. ** evil #+begin_quote Evil is an extensible vi layer for Emacs. It emulates the main features of Vim, and provides facilities for writing custom extensions. https://github.com/emacs-evil/evil #+end_quote #+begin_src emacs-lisp :lexical t (use-package evil :demand t :preface (setq evil-want-keybinding nil) :custom (evil-ex-visual-char-range t "limit text replacement in visual selections") (evil-symbol-word-search t "search by symbol with * and #.") (evil-shift-width 2 "Same behavior for vim's '<' and '>' commands") (evil-complete-all-buffers nil) (evil-want-integration t) (evil-want-C-i-jump t) (evil-search-module 'evil-search "use vim-like search instead of 'isearch") (evil-undo-system 'undo-redo) :hook (lisp-interaction-mode . (lambda () (setq-local evil-lookup-func #'+evil-lookup-elisp-symbol))) (emacs-lisp-mode . (lambda () (setq-local evil-lookup-func #'+evil-lookup-elisp-symbol))) :config (defun +evil-lookup-elisp-symbol () "Lookup elisp symbol at point." (if-let ((symbol (thing-at-point 'symbol))) (describe-symbol (intern symbol)) (user-error "No symbol at point"))) (+general-global-window "H" 'evil-window-move-far-left "J" 'evil-window-move-very-bottom "K" 'evil-window-move-very-top "L" 'evil-window-move-far-right) (+general-global-menu! "quit" "q" ":" 'evil-command-window-ex "/" 'evil-command-window-search-forward "?" 'evil-command-window-search-backward) ;;I want Emacs regular mouse click behavior (define-key evil-motion-state-map [down-mouse-1] nil) (evil-mode)) #+end_src *** evil-anzu-mode #+begin_quote anzu for evil-mode https://github.com/emacsorphanage/evil-anzu #+end_quote Shows match counts in mode line. #+begin_src emacs-lisp :lexical t (use-package evil-anzu :after (evil anzu)) #+end_src *** evil-collection :PROPERTIES: :ID: fe31fb46-abb8-4f19-ac06-9f1fd3b90f22 :END: #+begin_quote This is a collection of Evil bindings for the parts of Emacs that Evil does not cover properly by default. https://github.com/emacs-evil/evil-collection #+end_quote #+begin_src emacs-lisp :lexical t (use-package evil-collection :ensure (:remotes ("origin" ("fork" :repo "progfolio/evil-collection"))) :after (evil) :config (setq evil-collection-mode-list (remq 'elpaca evil-collection-mode-list)) (evil-collection-init) :init (setq evil-collection-setup-minibuffer t) :custom (evil-collection-elpaca-want-g-filters nil) (evil-collection-ement-want-auto-retro t)) #+end_src ** accord #+begin_src emacs-lisp :lexical t (use-package accord :ensure (accord :host github :repo "progfolio/accord" :protocol ssh) :general (+general-global-application "a" '(:ignore t :which-key "accord") "aa" 'accord) :config (global-leader :major-modes '(accord-mode) :keymaps '(accord-mode-map) "/" 'accord-channel-search ";" 'accord-channel-last "S" '(:ignore t :which-key "Server") "SN" 'accord-server-next "SP" 'accord-server-prev "c" '(:ignore t :which-key "channel") "c;" 'accord-channel-last "c/" 'accord-channel-search "cj" 'accord-channel-scroll-down "ck" 'accord-channel-scroll-up "cn" 'accord-channel-next "cp" 'accord-channel-prev "cr" 'accord-channel-mark-read "cu" 'accord-channel-goto-unread "cu" 'accord-channel-scroll-up ;; These look superfluous, but without the :which-key value ;; we're picking up names from org mode leader bindings... ;; Not sure if it's a bug with general.el or my init. "d" '(accord-delete-message :which-key "accord-delete-message") "e" '(accord-edit-message :which-key "accord-edit-message") "s" '(accord-send-message :which-key "accord-send-message")) (+general-global-application "a/" 'accord-channel-search "a;" 'accord-channel-last "ac" '(:ignore t :which-key "channel") "ac/" 'accord-channel-search "ac;" 'accord-channel-last "acj" 'accord-channel-scroll-down "ack" 'accord-channel-scroll-up "acn" 'accord-channel-next "acp" 'accord-channel-prev "acr" 'accord-channel-mark-read "acu" 'accord-channel-goto-unread "acu" 'accord-channel-scroll-up "ad" 'accord-delete-message "ae" 'accord-edit-message "as" 'accord-send-message) (add-to-list 'display-buffer-alist '("\\*accord\\*" display-buffer-below-selected (window-height . 0.20))) (defun accord-setup () (flyspell-mode) (visual-line-mode)) (add-hook 'accord-mode-hook 'accord-setup)) #+end_src ** afternoon-theme #+begin_quote Dark color theme with a deep blue background https://github.com/osener/emacs-afternoon-theme #+end_quote #+begin_src emacs-lisp :lexical t (use-package afternoon-theme :defer t) #+end_src ** almost-mono-themes #+begin_quote A collection of almost monochrome emacs themes in a couple of variants. https://github.com/cryon/almost-mono-themes #+end_quote #+begin_src emacs-lisp :lexical t (use-package almost-mono-themes :defer t) #+end_src ** anzu #+begin_quote anzu.el provides a minor mode which displays 'current match/total matches' in the mode-line in various search modes. This makes it easy to understand how many matches there are in the current buffer for your search query. #+end_quote #+begin_src emacs-lisp :lexical t (use-package anzu :defer 10 :config (global-anzu-mode)) #+end_src ** auth Ensure only encrypted .authinfo.gpg file is used #+begin_src emacs-lisp :lexical t (use-feature auth-source :defer t :custom (auth-sources '("~/.authinfo.gpg"))) #+end_src ** auto-fill-mode I usually want lines to wrap at 80 chars (Emacs defaults to 70). #+begin_src emacs-lisp :lexical t (use-feature simple :general (+general-global-toggle "f" 'auto-fill-mode) :custom (eval-expression-debug-on-error nil) (fill-column 80 "Wrap at 80 columns.")) #+end_src ** auto-revert Automatically revert a buffer if its file has changed on disk. This is useful when checking out different versions of a file in version control. It also helps if multiple instances of Emacs are editing the same file. #+begin_src emacs-lisp :lexical t (use-feature autorevert :defer 2 :custom (auto-revert-interval 0.01 "Instantaneously revert") :config (global-auto-revert-mode t)) #+end_src ** auto-tangle-mode #+begin_src emacs-lisp :lexical t (use-package auto-tangle-mode :ensure (auto-tangle-mode :host github :repo "progfolio/auto-tangle-mode.el" :local-repo "auto-tangle-mode") :commands (auto-tangle-mode)) #+end_src ** bookmark #+begin_src emacs-lisp :lexical t (use-feature bookmark :custom (bookmark-fontify nil) :general (+general-global-bookmark "j" 'bookmark-jump "s" 'bookmark-set "r" 'bookmark-rename)) #+end_src ** buttercup #+begin_quote Buttercup is a behavior-driven development framework for testing Emacs Lisp code. https://github.com/jorgenschaefer/emacs-buttercup #+end_quote #+begin_src emacs-lisp :lexical t (use-package buttercup :commands (buttercup-run-at-point)) #+end_src ** cape #+begin_quote Completion At Point Extensions #+end_quote #+begin_src emacs-lisp :lexical t (use-package cape :commands (cape-file) :general (general-define-key :keymaps '(insert) "C-x C-f" #'cape-file "C-x C-l" #'cape-line)) #+end_src ** cleanroom #+begin_src emacs-lisp :lexical t :tangle no (use-package cleanroom :ensure (cleanroom :host github :repo "progfolio/cleanroom" :protocol ssh) :defer t) #+end_src ** compile #+begin_src emacs-lisp :lexical t (use-feature compile :commands (compile recompile) :custom (compilation-scroll-output 'first-error) :config (defun +compilation-colorize () "Colorize from `compilation-filter-start' to `point'." (require 'ansi-color) (let ((inhibit-read-only t)) (ansi-color-apply-on-region (point-min) (point-max)))) (add-hook 'compilation-filter-hook #'+compilation-colorize)) #+end_src ** consult #+begin_quote Consulting completing-read #+end_quote #+begin_src emacs-lisp :lexical t (use-package consult :demand t :config ;;Credit to @alphapapa (defun +consult-info-emacs () "Search through Emacs info pages." (interactive) (consult-info "emacs" "efaq" "elisp" "cl")) (consult-customize consult-recent-file consult--source-recent-file consult--source-buffer consult--source-bookmark :preview-key nil) (global-leader :major-modes '(org-mode) :keymaps '(org-mode-map) "/" 'consult-org-heading) (global-definer "/" 'consult-line) (+general-global-buffer "b" 'consult-buffer) (+general-global-project "a" 'consult-grep) (+general-global-file ;;"f" 'consult-file "r" 'consult-recent-file)) #+end_src ** corfu #+begin_quote Completion Overlay Region FUnction "Corfu enhances completion at point with a small completion popup." https://github.com/minad/corfu #+end_quote #+begin_src emacs-lisp :lexical t (use-package corfu :ensure (corfu :host github :repo "minad/corfu" :files (:defaults "extensions/*")) :defer 5 :custom (corfu-cycle t) :config (global-corfu-mode) (with-eval-after-load 'evil (setq evil-complete-next-func (lambda (_) (completion-at-point))))) #+end_src ** custom-set-variables #+begin_src emacs-lisp :lexical t (use-feature cus-edit :custom (custom-file null-device "Don't store customizations")) #+end_src ** dictionary #+begin_src emacs-lisp :lexical t (use-feature dictionary :defer t :general (global-definer "W" 'dictionary-lookup-definition) (+general-global-application "D" 'dictionary-search) (+general-global-text "d" 'dictionary-search) :custom (dictionary-create-buttons nil) (dicitonary-use-single-buffer t)) #+end_src ** dired #+begin_quote Directory browsing commands #+end_quote #+begin_src emacs-lisp :lexical t (use-feature dired :commands (dired) :custom (dired-mouse-drag-files t) (dired-listing-switches "-alh" "Human friendly file sizes.") (dired-kill-when-opening-new-dired-buffer t) (dired-omit-files "\\(?:\\.+[^z-a]*\\)") :hook (dired-mode-hook . dired-omit-mode) :general (+general-global-application "d" 'dired)) #+end_src ** doct #+begin_quote doct is a function that provides an alternative, declarative syntax for describing Org capture templates. https://github.com/progfolio/doct #+end_quote #+begin_src emacs-lisp :lexical t (use-package doct :ensure (doct :branch "development" :protocol ssh :depth nil :autoloads nil) :commands (doct)) #+end_src ** doom-modeline #+begin_quote A fancy and fast mode-line inspired by minimalism design. https://github.com/seagle0128/doom-modeline #+end_quote #+begin_src emacs-lisp :lexical t (use-package doom-modeline :defer 2 :config (doom-modeline-mode) :custom (doom-modeline-time-analogue-clock nil) (doom-modeline-time-icon nil) (doom-modeline-unicode-fallback nil) (doom-modeline-buffer-encoding 'nondefault) (display-time-load-average nil) (doom-modeline-icon t "Show icons in the modeline")) #+end_src ** doom-themes #+begin_quote DOOM Themes is an opinionated UI plugin and pack of themes extracted from [hlissner's] emacs.d, inspired by some of my favorite color themes. https://github.com/hlissner/emacs-doom-themes #+end_quote #+begin_src emacs-lisp :lexical t (use-package doom-themes :ensure (:local-repo "doom-themes") :defer t) #+end_src ** edebug #+begin_quote This minor mode allows programmers to step through Emacs Lisp source code while executing functions. You can also set breakpoints, trace (stopping at each expression), evaluate expressions as if outside Edebug, reevaluate and display a list of expressions, trap errors normally caught by debug, and display a debug style backtrace. #+end_quote #+begin_src emacs-lisp :lexical t (use-feature edebug :config (global-leader :major-modes '(emacs-lisp-mode lisp-interaction-mode t) :keymaps '(emacs-lisp-mode-map lisp-interaction-mode-map) "d" '(:ignore t :which-key "debug") "dA" 'edebug-all-defs "db" '(:ignore t :which-key "breakpoint") "dbU" 'edebug-unset-breakpoints "dbc" 'edebug-set-conditional-breakpoint "dbg" 'edebug-set-global-break-condition "dbn" 'edebug-next-breakpoint "dbs" 'edebug-set-breakpoint "dbt" 'edebug-toggle-disable-breakpoint "dbu" 'edebug-unset-breakpoint "dw" 'edebug-where)) #+end_src ** ediff #+begin_src emacs-lisp :lexical t (use-feature ediff :defer t :custom (ediff-window-setup-function #'ediff-setup-windows-plain) (ediff-split-window-function #'split-window-horizontally) :config (add-hook 'ediff-quit-hook #'winner-undo)) #+end_src ** elfeed #+begin_quote Elfeed is an extensible web feed reader for Emacs, supporting both Atom and RSS. https://github.com/skeeto/elfeed #+end_quote I've put my elfeed database in under version control. I may move it from its default location (=~/.elfeed=), in =elfeed-db-directory=. #+begin_src emacs-lisp :lexical t (use-package elfeed :commands (elfeed) :config (defvar +elfeed-feed-file (expand-file-name "~/Documents/rss-feeds.org")) (setq elfeed-feeds (with-current-buffer (find-file-noselect +elfeed-feed-file) (save-excursion (save-restriction (org-fold-show-all) (goto-char (point-min)) (let ((found nil)) (org-element-map (org-element-parse-buffer) 'link (lambda (node) (when-let ((props (cadr node)) (standards (plist-get props :standard-properties)) (tags (org-get-tags (aref standards 0))) ((member "elfeed" tags)) ((not (member "ignore" tags)))) (push (cons (plist-get props :raw-link) (delq 'elfeed (mapcar #'intern tags))) found))) nil nil t) (nreverse found)))))) (defun +elfeed-play-in-mpv () "Play selected videos in a shared mpv instance in chronological order." (interactive) (mapc (lambda (entry) (emp-open-url (elfeed-entry-link entry)) (message "Playing %S in MPV" (elfeed-entry-title entry))) (nreverse (elfeed-search-selected))) (elfeed-search-untag-all-unread)) (defun +elfeed-download () "Download selected videos." (interactive) (let ((default-directory (expand-file-name "~/Videos/youtube"))) (dolist (entry (nreverse (elfeed-search-selected))) (let ((title (elfeed-entry-title entry))) (message "Attempting to download %S" (elfeed-entry-title entry)) (make-process :name "elfeed-download" :buffer "elfeed-download" :command (list "youtube-dl" (elfeed-entry-link entry)) :sentinel (lambda (process _event) (when (= 0 (process-exit-status process)) (message "Successfully downloaded %S" title)))))) (elfeed-search-untag-all-unread))) (general-define-key :states '(normal) :keymaps 'elfeed-search-mode-map "p" '+elfeed-play-in-mpv "d" '+elfeed-download) (general-define-key :states '(normal) :keymaps 'elfeed-show-mode-map "J" 'elfeed-show-next "K" 'elfeed-show-prev) :general (+general-global-application "e" 'elfeed)) #+end_src ** elisp-mode #+begin_src emacs-lisp :lexical t (use-feature elisp-mode :config (global-leader :major-modes '(emacs-lisp-mode lisp-interaction-mode t) :keymaps '(emacs-lisp-mode-map lisp-interaction-mode-map) "e" '(:ignore t :which-key "eval") "eb" 'eval-buffer "ed" 'eval-defun "ee" 'eval-expression "ep" 'pp-eval-last-sexp "es" 'eval-last-sexp "i" 'elisp-index-search)) #+end_src ** Emacs These settings defined in C code, so we use the ~emacs~ pseudo-package to set them. #+begin_src emacs-lisp :lexical t (use-feature emacs :demand t :custom (scroll-conservatively 101 "Scroll just enough to bring text into view") (enable-recursive-minibuffers t "Allow minibuffer commands in minibuffer") (frame-title-format '(buffer-file-name "%f" ("%b")) "Make frame title current file's name.") (find-library-include-other-files nil) (indent-tabs-mode nil "Use spaces, not tabs") (inhibit-startup-screen t) (history-delete-duplicates t "Don't clutter history") (pgtk-use-im-context-on-new-connection nil "Prevent GTK from stealing Shift + Space") (sentence-end-double-space nil "Double space sentence demarcation breaks sentence navigation in Evil") (tab-stop-list (number-sequence 2 120 2)) (tab-width 2 "Shorter tab widths") (completion-styles '(flex basic partial-completion emacs22)) (report-emacs-bug-no-explanations t) (report-emacs-bug-no-confirmation t)) #+end_src ** ement.el #+begin_quote Ement.el is a new Matrix client for Emacs. It aims to be simple, fast, featureful, and reliable. ~ https://github.com/alphapapa/ement.el #+end_quote #+begin_src emacs-lisp :lexical t (use-package ement :ensure (ement :host github :repo "alphapapa/ement.el" :branch nil) :commands (ement-connect) :custom (ement-room-avatars t) (ement-room-images t) (ement-room-compose-method 'compose-buffer) (ement-room-compose-buffer-window-auto-height-min 10) (ement-save-sessions t) (ement-sessions-file "~/.cache/ement.eld.gpg") :config (require 'ement-tabulated-room-list) (add-to-list 'display-buffer-alist (cons "^\\*Ement compose: " (cons 'display-buffer-below-selected '((window-height . 6) (inhibit-same-window . t) (reusable-frames . nil))))) (add-to-list 'display-buffer-alist '("\\*Ement image:" (display-buffer-reuse-window) (reusable-frames . nil))) (define-advice ement-disconnect (:around (fn &rest args) "no-epa-key-prompt") (let ((epa-file-select-keys 'symmetric)) (apply fn args))) (define-advice ement--kill-emacs-hook (:around (fn &rest args) "no-epa-key-prompt") (let ((epa-file-select-keys 'symmetric)) (apply fn args))) (+general-global-application ;; "c" for chat "cl" 'ement-room-list "cn" 'ement-notify-switch-to-notifications-buffer "cm" 'ement-notify-switch-to-mentions-buffer "cs" 'ement-directory-search) :general (+general-global-application ;; "c" for chat "c" '(:ignore t :which-key "Matrix") "ce" '(lambda () (interactive) (call-interactively (if (bound-and-true-p ement-sessions) #'ement-room-list #'ement-connect))))) #+end_src ** emp #+begin_quote MPV integration #+end_quote #+begin_src emacs-lisp :lexical t (use-package emp :ensure (emp :host github :repo "progfolio/emp") :config :general (+general-global-application "v" '(:ignore t :which-key "video/audio") "vQ" 'emp-kill "vf" '(:ignore t :which-key "frame") "vfb" 'emp-frame-back-step "vff" 'emp-frame-step "vi" 'emp-insert-playback-time "vo" 'emp-open "vO" 'emp-cycle-osd "v SPC" 'emp-pause "vs" 'emp-seek "vr" 'emp-revert-seek "vt" 'emp-seek-absolute "vv" 'emp-set-context "vS" 'emp-speed-set)) #+end_src ** epa/g-config #+begin_src emacs-lisp :lexical t (use-feature epg-config :defer t :init (setq epg-pinentry-mode 'loopback)) #+end_src #+begin_src emacs-lisp :lexical t (use-feature epa-file :defer t :init (setq epa-file-cache-passphrase-for-symmetric-encryption t)) #+end_src ** epoch #+begin_src emacs-lisp :lexical t (use-package epoch :ensure (epoch :host github :repo "progfolio/epoch") :after (org) :commands (epoch-todo epoch-agenda-todo)) #+end_src ** files By default Emacs saves backups in the current buffer's working directory. I'd rather have everything in one folder to keep my file system tidy. #+begin_src emacs-lisp :lexical t (use-feature files ;;:hook ;;(before-save . delete-trailing-whitespace) :config ;; source: http://steve.yegge.googlepages.com/my-dot-emacs-file (defun rename-file-and-buffer (new-name) "Renames both current buffer and file it's visiting to NEW-NAME." (interactive "sNew name: ") (let ((name (buffer-name)) (filename (buffer-file-name))) (if (not filename) (message "Buffer '%s' is not visiting a file." name) (if (get-buffer new-name) (message "A buffer named '%s' already exists." new-name) (progn (rename-file filename new-name 1) (rename-buffer new-name) (set-visited-file-name new-name) (set-buffer-modified-p nil)))))) :custom (require-final-newline t "Automatically add newline at end of file") (backup-by-copying t) (backup-directory-alist `((".*" . ,(expand-file-name (concat user-emacs-directory "backups")))) "Keep backups in their own directory") (auto-save-file-name-transforms `((".*" ,(concat user-emacs-directory "autosaves/") t))) (delete-old-versions t) (kept-new-versions 10) (kept-old-versions 5) (version-control t) (safe-local-variable-values '((eval load-file "./init-dev.el") (org-clean-refile-inherit-tags)) "Store safe local variables here instead of in emacs-custom.el")) #+end_src ** find-func #+begin_src emacs-lisp :lexical t (use-feature find-func :defer t :config (setq find-function-C-source-directory (expand-file-name "~/repos/emacs/src/"))) #+end_src ** fill-column-indicator #+begin_src emacs-lisp :lexical t (use-feature display-fill-column-indicator :custom (display-fill-column-indicator-character (plist-get '( triple-pipe ?┆ double-pipe ?╎ double-bar ?║ solid-block ?█ empty-bullet ?◦) 'triple-pipe)) :general (+general-global-toggle "F" '(:ignore t :which-key "fill-column-indicator") "FF" 'display-fill-column-indicator-mode "FG" 'global-display-fill-column-indicator-mode)) #+end_src ** flycheck #+begin_quote Flycheck is a modern on-the-fly syntax checking extension for GNU Emacs, intended as replacement for the older Flymake extension which is part of GNU Emacs. https://www.flycheck.org/en/latest/ #+end_quote #+begin_src emacs-lisp :lexical t (use-package flycheck :commands (flycheck-mode) :custom (flycheck-emacs-lisp-load-path 'inherit "necessary with alternatives to package.el")) #+end_src *** flycheck-package =package-lint= integration for flycheck. #+begin_src emacs-lisp :lexical t (use-package flycheck-package :after (flychceck) :config (flycheck-package-setup) (add-to-list 'display-buffer-alist '("\\*Flycheck errors\\*" display-buffer-below-selected (window-height . 0.15)))) #+end_src ** flymake #+begin_src emacs-lisp :lexical t (use-feature flymake :general (global-leader :major-modes '(emacs-lisp-mode lisp-interaction-mode t) :keymaps '(emacs-lisp-mode-map lisp-interaction-mode-map) "f" '(:ignore t :which-key "flymake") "ff" '((lambda () (interactive) (flymake-mode 'toggle)) :which-key "toggle flymake-mode") "fn" 'flymake-goto-next-error "fp" 'flymake-goto-prev-error) :hook (flymake-mode . (lambda () (or (ignore-errors flymake-show-project-diagnostics) (flymake-show-buffer-diagnostics)))) :config (add-to-list 'display-buffer-alist '("\\`\\*Flymake diagnostics.*?\\*\\'" display-buffer-in-side-window (window-parameters (window-height 0.10)) (side . bottom))) (defun +flymake-elpaca-bytecomp-load-path () "Augment `elisp-flymake-byte-compile-load-path' to support Elpaca." (setq-local elisp-flymake-byte-compile-load-path `("./" ,@(mapcar #'file-name-as-directory (nthcdr 2 (directory-files (expand-file-name "builds" elpaca-directory) 'full)))))) (add-hook 'flymake-mode-hook #'+flymake-elpaca-bytecomp-load-path)) #+end_src ** flymake-guile #+begin_src emacs-lisp :lexical t (use-package flymake-guile :defer t :hook (scheme-mode . flymake-guile)) #+end_src ** flyspell #+begin_src emacs-lisp :lexical t (use-feature flyspell :commands (flyspell-mode flyspell-prog-mode) :general (+general-global-toggle "ss" 'flyspell-mode "sp" 'flyspell-prog-mode) (+general-global-spelling "n" 'flyspell-goto-next-error "b" 'flyspell-buffer "w" 'flyspell-word "r" 'flyspell-region) :hook ((org-mode mu4e-compose-mode git-commit-mode) . flyspell-mode)) #+end_src *** flyspell-correct #+begin_quote "This package provides functionality for correcting words via custom interfaces." -- https://d12frosted.io/posts/2016-05-09-flyspell-correct-intro.html #+end_quote #+begin_src emacs-lisp :lexical t (use-package flyspell-correct :after (flyspell) :general (+general-global-spelling "B" 'flyspell-correct-wrapper "p" 'flyspell-correct-at-point)) #+end_src ** fontify-face #+begin_quote Fontify symbols representing faces with that face. https://github.com/Fuco1/fontify-face #+end_quote #+begin_src emacs-lisp :lexical t (use-package fontify-face :commands (fontify-face-mode)) #+end_src ** fountain-mode #+begin_quote Fountain Mode is a screenwriting program for GNU Emacs using the Fountain plain text markup format. https://github.com/rnkn/fountain-mode #+end_quote #+begin_src emacs-lisp :lexical t (use-package fountain-mode :mode "\\.fountain\\'") #+end_src ** forge #+begin_quote Work with Git forges from the comfort of Magit #+end_quote https://github.com/magit/forge #+begin_src emacs-lisp :lexical t (use-package transient :defer t) ;; Use MELPA until built-in version updated. (use-package forge :ensure (:files (:defaults "docs/*")) :after magit :init (setq forge-add-default-bindings nil forge-display-in-status-buffer nil forge-add-pullreq-refspec nil)) #+end_src ** geiser #+begin_src emacs-lisp :lexical t (use-package geiser :defer t :config (global-leader :major-modes '(scheme-mode) :keymaps '(scheme-mode-map) "e" '(:ignore t :which-key "eval") "eb" 'geiser-eval-buffer "es" 'geiser-eval-last-sexp "es" 'geiser-eval-region "i" 'geiser-doc-module "]" 'geiser-squarify)) #+end_src ** geiser-guile #+begin_src emacs-lisp :lexical t (use-package geiser-guile :defer t) #+end_src ** gnus #+begin_src emacs-lisp :lexical t (use-feature gnus :defer t :config (setq user-mail-address (+email-address +email-dev) gnus-select-method '(nntp "news.gmane.io") gnus-use-dribble-file nil gnus-thread-sort-functions '(gnus-thread-sort-by-most-recent-date (not gnus-thread-sort-by-number)) gnus-use-cache t gnus-interactive-exit nil gnus-ignored-newsgroups "^to\\.\\|^[0-9. ]+\\( \\|$\\)\\|^[\"]\"[#'()]") (require 'nnimap) (add-to-list 'gnus-secondary-select-methods '(nntp "news.gmane.io")) (add-to-list 'gnus-secondary-select-methods '(nnimap "gmail" (nnimap-address "imap.gmail.com") (nnimap-server-port "imaps") (nnimap-stream ssl) (nnir-search-engine imap) (gnus-topic-set-parameters "gmail" '((display . 200))) (nnmail-expiry-target "nnimap+gmail:[Gmail]/Trash") (nnmail-expiry-wait 90)))) #+end_src ** help #+begin_src emacs-lisp :lexical t (use-feature help :defer 1 :custom (help-enable-variable-value-editing t) (help-window-select t "Always select the help window")) #+end_src ** history #+begin_src emacs-lisp :lexical t (use-feature savehist :defer 1 :config (savehist-mode 1)) #+end_src ** holidays I'd like to see holidays and anniversaries in my org-agenda and calendar I've removed the default holiday lists that I don't need. #+begin_src emacs-lisp :lexical t (use-feature holidays :commands (org-agenda) :custom (holiday-bahai-holidays nil) (holiday-hebrew-holidays nil) (holiday-islamic-holidays nil) (holiday-oriental-holidays nil)) #+end_src ** htmlize #+begin_quote This package converts the buffer text and the associated decorations to HTML. https://github.com/hniksic/emacs-htmlize #+end_quote This is necessary for exporting Org files to HTML. #+begin_src emacs-lisp :lexical t (use-package htmlize :defer t) #+end_src ** ielm #+begin_quote Provides a nice interface to evaluating Emacs Lisp expressions. Input is handled by the comint package, and output is passed through the pretty-printer. ielm.el commentary #+end_quote #+begin_src emacs-lisp :lexical t (use-feature ielm :config (global-leader :major-modes '(inferior-emacs-lisp-mode) :keymaps '(inferior-emacs-lisp-mode-map) "b" '(:ignore t :which-key "buffer") "bb" 'ielm-change-working-buffer "bd" 'ielm-display-working-buffer "bp" 'ielm-print-working-buffer "c" 'comint-clear-buffer) ;;@TODO: fix this command. ;;This should be easier :general (+general-global-application "i" '("ielm" . (lambda () (interactive) (let* ((b (current-buffer)) (i (format "*ielm<%s>*" b))) (setq ielm-prompt (concat (buffer-name b) ">")) (ielm i) (ielm-change-working-buffer b) (next-buffer) (switch-to-buffer-other-window i)))))) #+end_src ** i3wm-config-mode #+begin_quote An expansion of conf-mode to bring proper syntax highlighting to your i3wm config. #+end_quote #+begin_src emacs-lisp :lexical t (use-package i3wm-config-mode :ensure (i3wm-config-mode :host github :repo "Alexander-Miller/i3wm-Config-Mode") :commands (i3wm-config-mode)) #+end_src ** js2 #+begin_quote Improved JavaScript editing mode for GNU Emacs https://github.com/mooz/js2-mode #+end_quote #+begin_src emacs-lisp :lexical t (use-package js2-mode :commands (js2-mode) :mode "\\.js\\'" :interpreter (("nodejs" . js2-mode) ("node" . js2-mode))) #+end_src *** TODO May not need this on Emacs 27+ Need to investigate, but js-mode might have been fixed/updated. ** lilypond Major mode for Lilypond music engraver. Installing Lilypond puts these in =/usr/share/Emacs/site-lisp=, but I plan on editing these. #+begin_src emacs-lisp :lexical t :tangle no (use-package lilypond-mode :ensure (lilypond-mode :host gitlab :repo "lilypond/lilypond" :remotes (("fork" :host github :repo "progfolio/lilypond" :branch "fix/lilypond-mode" :protocol ssh) "origin") :files ("elisp/*.el" "elisp/out/lilypond-words.el") :protocol ssh :pre-build (("./autogen.sh" "--noconfigure") ("./configure") ("bash" "-c" "cd elisp && make && \ echo ';; Local Variables:\n;; no-byte-compile: t\n;; End:' \ >> out/lilypond-words.el"))) :mode "\\.ly\\'" :general (global-leader :major-modes '(lilypond-mode t) :keymaps '(lilypond-mode-map) "l" 'lilypond-compile-file "p" 'lilypond-play)) #+end_src ** macrostep #+begin_quote macrostep is an Emacs minor mode for interactively stepping through the expansion of macros in Emacs Lisp source code. https://github.com/joddie/macrostep #+end_quote #+begin_src emacs-lisp :lexical t (use-package macrostep :config (global-leader :major-modes '(emacs-lisp-mode lisp-interaction-mode t) :keymaps '(emacs-lisp-mode-map lisp-interaction-mode-map) "m" '(:ignore t :which-key "macrostep") "me" 'macrostep-expand "mc" 'macrostep-collapse "mj" 'macrostep-next-macro "mk" 'macrostep-prev-macro)) #+end_src ** magit #+begin_quote Magit is an interface to the version control system Git, implemented as an Emacs package. https://magit.vc/ #+end_quote #+begin_src emacs-lisp :lexical t (use-package magit :defer t :after (general) :custom (magit-repository-directories (list (cons elpaca-repos-directory 1))) (magit-diff-refine-hunk 'all) :general (+general-global-git/version-control "b" 'magit-branch "B" 'magit-blame "c" 'magit-clone "f" '(:ignore t :which-key "file") "ff" 'magit-find-file "fh" 'magit-log-buffer-file "i" 'magit-init "L" 'magit-list-repositories "m" 'magit-dispatch "S" 'magit-stage-file "s" 'magit-status "U" 'magit-unstage-file) :config (transient-bind-q-to-quit)) #+end_src ** marginalia #+begin_quote Enrich existing commands with completion annotations #+end_quote #+begin_src emacs-lisp :lexical t (use-package marginalia :defer 2 :config (marginalia-mode)) #+end_src ** markdown #+begin_quote markdown-mode is a major mode for editing Markdown-formatted text. https://jblevins.org/projects/markdown-mode/ #+end_quote #+begin_src emacs-lisp :lexical t (use-package markdown-mode :commands (markdown-mode gfm-mode) :mode (("README\\.md\\'" . gfm-mode) ("\\.md\\'" . markdown-mode) ("\\.markdown\\'" . markdown-mode)) :custom (markdown-command "/usr/bin/pandoc")) #+end_src ** minibuffer #+begin_src emacs-lisp :lexical t (use-feature minibuffer :custom (read-file-name-completion-ignore-case t) :config (defun +minibuffer-up-dir () "Trim rightmost directory component of `minibuffer-contents'." (interactive) (unless (minibufferp) (user-error "Minibuffer not selected")) (let* ((f (directory-file-name (minibuffer-contents))) (s (file-name-directory f))) (delete-minibuffer-contents) (when s (insert s)))) (define-key minibuffer-local-filename-completion-map (kbd "C-h") #'+minibuffer-up-dir) (minibuffer-depth-indicate-mode)) #+end_src ** miscellany A package for miscellaneous functions. #+begin_src emacs-lisp :lexical t (use-package miscellany :ensure (miscellany :host github :repo "progfolio/miscellany.el" :local-repo "miscellany" :protocol ssh :branch "master" :pre-build ((require 'ob-tangle) (setq org-confirm-babel-evaluate nil) (org-babel-tangle-file "./miscellany.org"))) :after (general) :commands (+alternate-buffer +change-theme +compute-blood-pressure-table-row +kill-other-buffers +org-fix-close-times +org-remove-timestamp-time +org-toggle-hide-emphasis-markers +recompile-user-package-dir +server-eval-all +toggle-maximize-buffer +toggle-relative-lines +toggle-syntax-highlighting +universal-arg) :general (global-definer "u" '(+universal-arg :which-key "universal-arg")) (+general-global-buffer "a" '(+alternate-buffer :which-key "alternate-buffer") "q" 'bury-buffer "m" '(+kill-other-buffers :which-key "kill-other-buffers") "N" '(+normalize-buffer :which-key "normalize-buffer") "u" 'unbury-buffer) (+general-global-window "f" '(+toggle-maximize-buffer :which-key "toggle-maximize-buffer")) (+general-global-toggle "S" '(+toggle-syntax-highlighting :which-key "syntax-highlighting") "m" '(+toggle-mode :which-key "mode") "n" '(+toggle-relative-lines :which-key "relative-lines") "t" '(:ignore t :which-key "theme") "tt" '(+change-theme :which-key "toggle-theme") "tn" '((lambda () (interactive) (+theme-nth 1)) :which-key "theme-next") "tp" '((lambda () (interactive) (+theme-nth -1)) :which-key "theme-prev"))) #+end_src ** mu4e #+begin_quote An emacs-based e-mail client which uses mu as its back-end. https://www.djcbsoftware.nl/code/mu/mu4e.html #+end_quote #+begin_src emacs-lisp :lexical t :tangle yes (use-package mu4e :ensure `(mu4e :host github :files ("mu4e/*.el" "build/mu4e/mu4e-meta.el" "build/mu4e/mu4e-config.el" "build/mu4e/mu4e.info") :repo "djcb/mu" :main "mu4e/mu4e.el" :pre-build (("./autogen.sh" "-Dtests=disabled") ("ninja" "-C" "build") (make-symbolic-link (expand-file-name "./build/mu/mu") (expand-file-name "~/bin/mu") 'ok-if-exists)) :build (:not elpaca--compile-info) :post-build (("mu" "init" "--quiet" "--maildir" ,(concat (getenv "HOME") "/Documents/emails") "--my-address=" ,(+email-address +email-personal) "--my-address=" ,(+email-address +email-work) "--my-address=" ,(+email-address +email-dev)) ("mu" "index" "--quiet"))) :commands (mu4e mu4e-update-index) :custom (message-kill-buffer-on-exit t) (mu4e-update-interval 900 "Update every fifteen minutes") (mail-user-agent 'mu4e-user-agent "Use mu4e as default email program.") (mu4e-org-support t) (mu4e-maildir (expand-file-name "~/Documents/emails/")) (mu4e-attachment-dir "~/Downloads") (mu4e-completing-read-function 'completing-read) (mu4e-compose-signature-auto-include nil) (mu4e-use-fancy-chars t) (mu4e-view-show-addresses t) (mu4e-view-show-images t) (mu4e-sent-messages-behavior 'sent) (mu4e-get-mail-command "mbsync -c ~/.mbsyncrc -a") (mu4e-change-filenames-when-moving t "Needed for mbsync") (mu4e-confirm-quit nil) (mu4e-html2text-command 'mu4e-shr2text) (mu4e-context-policy 'pick-first) (mu4e-compose-context-policy 'always-ask) :config (add-to-list 'display-buffer-alist `(,(regexp-quote mu4e-main-buffer-name) display-buffer-same-window)) (setq mu4e-contexts (list (make-mu4e-context :name "personal" :enter-func (lambda () (mu4e-message "Entering personal context")) :leave-func (lambda () (mu4e-message "Leaving personal context")) :match-func (lambda (msg) (when msg (mu4e-message-contact-field-matches msg '(:from :to :cc :bcc) (+email-account +email-personal)))) :vars `((user-mail-address . ,(+email-address +email-personal)) (user-full-name . ,(+email-name +email-personal)) (mu4e-compose-format-flowed . t) (message-send-mail-function . smtpmail-send-it) (smtpmail-smtp-user . ,(+email-account +email-personal)) (smtpmail-auth-credentials . (expand-file-name "~/.authinfo.gpg")) (smtpmail-smtp-server . "smtp.gmail.com") (smtpmail-smtp-service . 587) (smtpmail-debug-info . t) (smtpmail-debug-verbose . t))) (make-mu4e-context :name "work" :enter-func (lambda () (mu4e-message "Entering work context")) :leave-func (lambda () (mu4e-message "Leaving work context")) :match-func (lambda (msg) (when msg (mu4e-message-contact-field-matches msg '(:from :to :cc :bcc) (+email-account +email-work)))) :vars `((user-mail-address . ,(+email-address +email-work)) (user-full-name . ,(+email-name +email-work)) (mu4e-compose-format-flowed . t) (message-send-mail-function . smtpmail-send-it) (smtpmail-smtp-user . ,(+email-account +email-work)) (smtpmail-starttls-credentials . (("smtp.gmail.com" 587 nil nil))) (smtpmail-auth-credentials . (expand-file-name "~/.authinfo.gpg")) (smtpmail-default-smtp-server . "smtp.gmail.com") (smtpmail-smtp-server . "smtp.gmail.com") (smtpmail-smtp-service . 587) (smtpmail-debug-info . t) (smtpmail-debug-verbose . t))) (make-mu4e-context :name "dev" :enter-func (lambda () (mu4e-message "Entering dev context")) :leave-func (lambda () (mu4e-message "Leaving dev context")) :match-func (lambda (msg) (when msg (mu4e-message-contact-field-matches msg '(:from :to :cc :bcc) (+email-account +email-dev)))) :vars `((user-mail-address . ,(+email-address +email-dev)) (user-full-name . ,(+email-name +email-dev)) (smtpmail-smtp-user . ,(+email-address +email-dev)) (smtpmail-auth-credentials . (expand-file-name "~/.authinfo.gpg")) (smtpmail-smtp-server . "smtp.purelymail.com") (smtpmail-smtp-service . 587) (smtpmail-stream-type . starttls) (mu4e-compose-format-flowed . t))))) (add-to-list 'mu4e-view-actions '("ViewInBrowser" . mu4e-action-view-in-browser) t) (add-to-list 'mu4e-bookmarks '( :name "straight.el" :query "list:straight.el.radian-software.github.com" :key ?s) 'append) (add-to-list 'mu4e-bookmarks '( :name "Elpaca" :query "list:elpaca.progfolio.github.com" :key ?e) 'append) (defun +mu4e-view-settings () "Settings for mu4e-view-mode." (visual-line-mode) (olivetti-mode) (variable-pitch-mode)) (add-hook 'mu4e-view-mode-hook #'+mu4e-view-settings) ;;Fold all threads by default. ;;(add-hook 'mu4e-thread-mode-hook #'mu4e-thread-fold-all) (global-leader :keymaps '(mu4e-compose-mode-map) "a" 'mml-attach-file) :general (+general-global-application "m" 'mu4e :which-key "mail")) #+end_src ** nov (epub) #+begin_quote Major mode for reading EPUB files in Emacs https://depp.brause.cc/nov.el/ #+end_quote #+begin_src emacs-lisp :lexical t (use-package nov :ensure (nov :depth nil) :custom (nov-text-width 80) :mode ("\\.epub\\'" . nov-mode) :commands (nov-mode)) #+end_src ** novice This feature tries to help new users by disabling certain potentially destructive or confusing commands. Don't need it. #+begin_src emacs-lisp :lexical t (use-feature novice :custom (disabled-command-function nil "Enable all commands")) #+end_src ** olivetti #+begin_quote A simple Emacs minor mode for a nice writing environment. https://github.com/rnkn/olivetti #+end_quote #+begin_src emacs-lisp :lexical t (use-package olivetti :commands (olivetti-mode)) #+end_src ** orderless #+begin_quote Completion style for matching regexps in any order #+end_quote #+begin_src emacs-lisp :lexical t (use-package orderless :defer 1 :custom (completion-styles '(orderless basic)) (completion-category-overrides '((file (styles partial-completion))))) #+end_src ** org #+begin_src emacs-lisp :lexical t (use-package org :ensure (:autoloads "org-loaddefs.el") :defer t :general ;; is for GUI only. TAB maps to C-i on terminals. (+general-global-application "o" '(:ignore t :which-key "org") "oc" 'org-capture "oC" '+org-capture-again "oi" 'org-insert-link "oj" 'org-chronicle "ok" '(:ignore t :which-key "clock") "okg" 'org-clock-goto "oki" 'org-clock-in-last "okj" 'org-clock-jump-to-current-clock "oko" 'org-clock-out "okr" 'org-resolve-clocks "ol" 'org-store-link "om" 'org-tags-view "os" 'org-search-view "oT" 'org-todo-list "ot" '(:ignore t :which-key "timer") "ott" 'org-timer "otS" 'org-timer-stop "otC" 'org-timer-change-times-in-region "otc" 'org-timer-set-timer "ots" 'org-timer-start "oti" 'org-timer-item "otp" 'org-timer-pause-or-continue "otr" 'org-timer-show-remaining-time) :config (general-define-key :states '(normal) :keymaps 'org-mode-map (kbd "") 'org-cycle (kbd "") 'org-shifttab) (general-define-key :states '(normal insert) :keymaps 'org-mode-map (kbd "M-l") 'org-metaright (kbd "M-h") 'org-metaleft (kbd "M-k") 'org-metaup (kbd "M-j") 'org-metadown (kbd "M-L") 'org-shiftmetaright (kbd "M-H") 'org-shiftmetaleft (kbd "M-K") 'org-shiftmetaup (kbd "M-J") 'org-shiftmetadown) (general-define-key :states '(motion) :keymaps 'org-mode-map (kbd "RET") 'org-open-at-point) (global-leader ;;for terminals :keymaps '(org-mode-map) "TAB" 'org-cycle "." 'org-time-stamp "!" 'org-time-stamp-inactive "<" 'org-date-from-calendar ">" 'org-goto-calendar "C" '(:ignore t :which-key "clock") "Cc" 'org-clock-cancel "Ci" 'org-clock-in "Co" 'org-clock-out "Cr" 'org-clock-report "CR" 'org-resolve-clocks "d" '(:ignore t :which-key "dates") "dd" 'org-deadline "df" '((lambda () (interactive) (+org-fix-close-times)) :which-key "org-fix-close-time") "ds" 'org-schedule "di" 'org-time-stamp-inactive "dt" 'org-time-stamp "e" '(:ignore t :which-key "export") "ee" 'org-export-dispatch "h" '(:ignore t :which-key "heading") "hf" 'org-forward-heading-same-level "hb" 'org-backward-heading-same-level "i" '(:ignore t :which-key "insert") "id" 'org-insert-drawer "ie" 'org-set-effort "if" 'org-footnote-new "iH" 'org-insert-heading-after-current "ih" 'org-insert-heading "ii" 'org-insert-item "il" 'org-insert-link "in" 'org-add-note "ip" 'org-set-property "is" 'org-insert-structure-template "it" 'org-set-tags-command "n" '(:ignore t :which-key "narrow") "nb" 'org-narrow-to-block "ne" 'org-narrow-to-element "ns" 'org-narrow-to-subtree "nt" 'org-toggle-narrow-to-subtree "nw" 'widen "s" '(:ignore t :which-key "trees/subtrees") "sA" 'org-archive-subtree "sa" 'org-toggle-archive-tag "sb" 'org-tree-to-indirect-buffer "sc" 'org-cut-subtree "sh" 'org-promote-subtree "sj" 'org-move-subtree-down "sk" 'org-move-subtree-up "sl" 'org-demote-subtree "sp" '(:ignore t :which-key "priority") "spu" 'org-priority-up "spd" 'org-priority-down "sps" 'org-priority-show "sm" 'org-match-sparse-tree "sn" 'org-toggle-narrow-to-subtree "sr" 'org-refile "sS" 'org-sort "ss" '+org-sparse-tree "t" '(:ignore t :which-key "tables") "ta" 'org-table-align "tb" 'org-table-blank-field "tc" 'org-table-convert "td" '(:ignore t :which-key "delete") "tdc" 'org-table-delete-column "tdr" 'org-table-kill-row "tE" 'org-table-export "te" 'org-table-eval-formula "tH" 'org-table-move-column-left "th" 'org-table-previous-field "tI" 'org-table-import "ti" '(:ignore t :which-key "insert") "tic" 'org-table-insert-column "tih" 'org-table-insert-hline "tiH" 'org-table-hline-and-move "tir" 'org-table-insert-row "tJ" 'org-table-move-row-down "tj" 'org-table-next-row "tK" 'org-table-move-row-up "tL" 'org-table-move-column-right "tl" 'org-table-next-field "tN" 'org-table-create-with-table.el "tn" 'org-table-create "tp" 'org-plot/gnuplot "tr" 'org-table-recalculate "ts" 'org-table-sort-lines "tt" '(:ignore t :which-key "toggle") "ttf" 'org-table-toggle-formula-debugger "tto" 'org-table-toggle-coordinate-overlays "tw" 'org-table-wrap-region "T" '(:ignore t :which-key "toggle") "Tc" 'org-toggle-checkbox "Te" 'org-toggle-pretty-entities "TE" '+org-toggle-hide-emphasis-markers "Th" 'org-toggle-heading "Ti" 'org-toggle-item "TI" 'org-toggle-inline-images "Tl" 'org-toggle-link-display "TT" 'org-todo "Tt" 'org-show-todo-tree "Tx" 'org-latex-preview "RET" 'org-ctrl-c-ret "#" 'org-update-statistics-cookies "'" 'org-edit-special "*" 'org-ctrl-c-star "-" 'org-ctrl-c-minus "A" 'org-attach) (defun +org-sparse-tree (&optional arg type) (interactive) (funcall #'org-sparse-tree arg type) (org-remove-occur-highlights)) (defun +insert-heading-advice (&rest _args) "Enter insert mode after org-insert-heading. Useful so I can tab to control level of inserted heading." (when evil-mode (evil-insert 1))) (advice-add #'org-insert-heading :after #'+insert-heading-advice) (defun +org-update-cookies () (interactive) (org-update-statistics-cookies "ALL")) ;; Offered a patch to fix this upstream. Too much bikeshedding for such a simple fix. (defun +org-tags-crm (fn &rest args) "Workaround for bug which excludes \",\" when reading tags via `completing-read-multiple'. I offered a patch to fix this, but it was met with too much resistance to be worth pursuing." (let ((crm-separator "\\(?:[[:space:]]*[,:][[:space:]]*\\)")) (unwind-protect (apply fn args) (advice-remove #'completing-read-multiple #'+org-tags-crm)))) (define-advice org-set-tags-command (:around (fn &rest args) comma-for-crm) (advice-add #'completing-read-multiple :around #'+org-tags-crm) (apply fn args)) :custom ;;default: ;;(org-w3m org-bbdb org-bibtex org-docview org-gnus org-info org-irc org-mhe org-rmail) ;;org-toc is interesting, but I'm not sure if I need it. (org-modules '(org-habit)) (org-todo-keywords '((sequence "TODO(t)" "STARTED(s!)" "NEXT(n!)" "BLOCKED(b@/!)" "|" "DONE(d)") (sequence "IDEA(i)" "|" "CANCELED(c@/!)" "DELEGATED(D@/!)") (sequence "RESEARCH(r)" "|")) ;;move to theme? org-todo-keyword-faces `(("CANCELED" . (:foreground "IndianRed1" :weight bold)) ("TODO" . (:foreground "#ffddaa" :weight bold :background "#202020" :box (:line-width 3 :width -2 :style released-button))))) (org-ellipsis (nth 5 '("↴" "˅" "…" " ⬙" " ▽" "▿"))) (org-priority-lowest ?D) (org-fontify-done-headline t) (org-M-RET-may-split-line nil "Don't split current line when creating new heading")) #+end_src ** org-agenda #+begin_src emacs-lisp :lexical t (use-feature org-agenda :after (general evil) :config (defun +org-agenda-archives (&optional arg) "Toggle `org-agenda-archives-mode' so that it includes archive files by default. Inverts normal logic of ARG." (interactive "P") (let ((current-prefix-arg (unless (or org-agenda-archives-mode arg) '(4)))) (call-interactively #'org-agenda-archives-mode))) (defun +org-agenda-place-point () "Place point on first agenda item." (goto-char (point-min)) (org-agenda-find-same-or-today-or-agenda)) (add-hook 'org-agenda-finalize-hook #'+org-agenda-place-point 90) (global-leader :keymaps 'org-mode-map "a" 'org-agenda) :general <> :custom <>) #+end_src *** keybindings :PROPERTIES: :header-args: :noweb-ref org-agenda-keybindings :END: #+begin_src emacs-lisp :lexical t (+general-global-application "o#" 'org-agenda-list-stuck-projects "o/" 'org-occur-in-agenda-files "oa" '((lambda () (interactive) (org-agenda nil "a")) :which-key "agenda") "oe" 'org-store-agenda-views "oo" 'org-agenda) #+end_src #+begin_src emacs-lisp :lexical t #+end_src Consider cribbing =evilified-state= from Spacemacs? #+begin_src emacs-lisp :lexical t (with-eval-after-load 'org-agenda (evil-make-intercept-map org-agenda-mode-map) (general-define-key :keymaps 'org-agenda-mode-map ;;:states '(emacs normal motion) "A" '+org-agenda-archives "C" 'org-agenda-clockreport-mode "D" 'org-agenda-goto-date "E" 'epoch-agenda-todo "H" 'org-habit-toggle-habits "J" 'org-agenda-next-item "K" 'org-agenda-previous-item "R" 'org-agenda-refile "S" 'org-agenda-schedule "RET" 'org-agenda-recenter "a" '+org-capture-again "c" 'org-agenda-capture "j" 'org-agenda-next-line "k" 'org-agenda-previous-line "m" 'org-agenda-month-view "t" 'org-agenda-set-tags "T" 'org-agenda-todo "u" 'org-agenda-undo)) #+end_src When saving, I want changes to my org-files reflected in any open org agenda buffers. #+begin_src emacs-lisp :lexical t :config ;;for org-agenda-icon-alist (evil-set-initial-state 'org-agenda-mode 'normal) (defun +org-agenda-redo-all () "Rebuild all agenda buffers" (interactive) (dolist (buffer (buffer-list)) (with-current-buffer buffer (when (derived-mode-p 'org-agenda-mode) (org-agenda-maybe-redo))))) (add-hook 'org-mode-hook (lambda () (add-hook 'after-save-hook '+org-agenda-redo-all nil t))) #+end_src *** :custom :PROPERTIES: :header-args: :noweb-ref org-agenda-custom :END: Add a custom view for a simplified work agenda. #+begin_src emacs-lisp :lexical t (org-agenda-window-setup 'current-window) (org-agenda-custom-commands '(("w" "Work Schedule" agenda "+work" ((org-agenda-files '("~/Documents/todo/work.org")) (org-agenda-window-setup 'current-window) (org-agenda-span 'week) (org-mode-hook nil) (org-agenda-start-on-weekday 2) (org-agenda-timegrid-use-ampm t) (org-agenda-time-leading-zero t) (org-agenda-use-time-grid nil) (org-agenda-archives-mode t) (org-agenda-weekend-days '(2 3)) (org-agenda-format-date "%a %m-%d") (org-agenda-prefix-format '((agenda . " %t"))) (org-agenda-finalize-hook '((lambda () "Format custom agenda command for work schedule." (save-excursion (goto-char (point-min)) (while (re-search-forward "TODO Work" nil 'noerror) (replace-match "")) (goto-char (point-min)) (forward-line) ;skip header (while (not (eobp)) (when (get-text-property (point) 'org-agenda-date-header) (let (fn) (save-excursion (forward-line) (setq fn (cond ((or (eobp) (get-text-property (point) 'org-agenda-date-header)) (lambda () (end-of-line) (insert " OFF"))) ((get-text-property (point) 'time) (lambda () (forward-line) (join-line)))))) (funcall fn))) (forward-line)))))))) ("n" "Agenda and all TODOs" ((agenda "") (alltodo ""))))) #+end_src #+begin_src emacs-lisp :lexical t (org-agenda-skip-deadline-prewarning-if-scheduled nil "Show approaching deadlines even when scheduled.") #+end_src I prefer the agenda to start on the current day view instead of the week. It's generally faster to generate and usually what I want. #+begin_src emacs-lisp :lexical t (org-agenda-span 'day) #+end_src These settings should speed up agenda generation: #+begin_src emacs-lisp :lexical t (org-agenda-inhibit-startup t) #+end_src But, I'm not sure about this one. It doesn't seem to speed things up that much for me and I like to see inherited tags on tasks. #+begin_src emacs-lisp :lexical t (org-agenda-use-tag-inheritance nil) #+end_src I find category icons to be a nice visual shorthand that keeps the agenda less cluttered. #+begin_src emacs-lisp :lexical t (org-agenda-prefix-format '((agenda . " %i %?-12t% s"))) (org-agenda-category-icon-alist (let ((image-dir (expand-file-name "images/org/" user-emacs-directory)) (categories '(("[Aa]ccounting" "accounting.svg") ("[Bb]irthday" "birthday.svg") ("[Cc]alendar" "calendar.svg") ("[Cc]hore" "chore.svg" :height 25) ("[Ee]xercise" "exercise.svg" :height 24) ("[Ff]ood" "food.svg") ("[Hh]abit" "habit.svg") ("[Hh]ealth" "health.svg") ("[Ii]n" "in.svg") ("[Ll]isten" "listen.svg") ("[Oo]ut" "out.svg") ("[Pp]lay" "play.svg") ("[Rr]efile" "refile.svg") ("[Rr]ead" "read.svg") ("[Ww]atch" "watch.svg") ("[Ww]ork" "work.svg")))) (mapcar (lambda (category) (list (nth 0 category) (expand-file-name (nth 1 category) image-dir) 'svg nil :height (or (plist-get category :height) 20) :ascent (or (plist-get category :ascent) 'center))) categories))) #+end_src This sorting strategy will place habits in/next to the agenda time-grid. #+begin_src emacs-lisp :lexical t (org-agenda-sorting-strategy '((agenda time-up priority-down category-keep) (todo priority-down category-keep) (tags priority-down category-keep) (search category-keep))) #+end_src I want the agenda clock report table to skip files that don't have any time clocked for the current agenda view. #+begin_src emacs-lisp :lexical t (org-agenda-clockreport-parameter-plist '(:link t :maxlevel 2 :stepskip0 t :fileskip0 t)) #+end_src I don't need to see the word "Scheduled" before scheduled items. #+begin_src emacs-lisp :lexical t (org-agenda-scheduled-leaders '("" "%2dx ")) #+end_src Align tags to column 80 in the agenda view: #+begin_src emacs-lisp :lexical t (org-agenda-tags-column -80) #+end_src *** TODO fix all-the-icons loading? Is it necessary? Save glyph locally? ** org-babel *** Tangling #+begin_src emacs-lisp :lexical t (use-feature ob-tangle :after (org) :custom (org-src-window-setup 'current-window) (org-src-preserve-indentation t) :general (global-leader :keymaps 'org-mode-map "b" '(:ignore t :which-key "babel") "bt" 'org-babel-tangle "bT" 'org-babel-tangle-file "be" '(:ignore t :which-key "execute") "beb" 'org-babel-execute-buffer "bes" 'org-babel-execute-subtree) :config <>) #+end_src *** config :PROPERTIES: :header-args: :noweb-ref org-babel-config :END: **** Structured Templates I want language specific code block templates. I may use yasnippets for this later to have more flexibility. #+begin_src emacs-lisp :lexical t (dolist (template '(("f" . "src fountain") ("se" . "src emacs-lisp :lexical t") ("ss" . "src shell") ("sj" . "src javascript"))) (add-to-list 'org-structure-template-alist template)) #+end_src **** Languages If =C-c-c= refuses to run code blocks and you get an error message: #+begin_quote "evaluation of language x disabled" #+end_quote delete the =*.el(c)= files and restart #+begin_src emacs-lisp :lexical t (use-feature ob-js :commands (org-babel-execute:js)) (use-feature ob-python :commands (org-babel-execute:python)) (use-feature ob-shell :commands (org-babel-execute:bash org-babel-execute:shell org-babel-expand-body:generic) :config (add-to-list 'org-babel-load-languages '(shell . t)) (org-babel-do-load-languages 'org-babel-load-languages org-babel-load-languages)) #+end_src ** org-capture #+begin_src emacs-lisp :lexical t (use-feature org-capture :config (define-advice org-capture-fill-template (:around (fn &rest args) comma-for-crm) (advice-add #'completing-read-multiple :around #'+org-tags-crm) (apply fn args)) (add-hook 'org-capture-mode-hook #'evil-insert-state) #+end_src Utility functions for use inside Org capture templates. #+begin_src emacs-lisp :lexical t (defun +org-schedule-relative-to-deadline () "For use with my appointment capture template. User is first prompted for an optional deadline. Then an optional schedule time. The scheduled default time is the deadline. This makes it easier to schedule relative to the deadline using the -- or ++ operators. Quitting during either date prompt results in an empty string for that prompt." (interactive) (condition-case nil (org-deadline nil) (quit nil)) (let ((org-overriding-default-time (or (org-get-deadline-time (point)) org-overriding-default-time))) (org-schedule nil (org-element-interpret-data (org-timestamp-from-time org-overriding-default-time (and org-overriding-default-time 'with-time)))) (let ((org-log-reschedule nil)) (condition-case nil (org-schedule nil) (quit (org-schedule '(4))))))) (defun +org-capture-again (&optional arg) "Call `org-capture' with last selected template. Pass ARG to `org-capture'. If there is no previous template, call `org-capture'." (interactive "P") (org-capture arg (plist-get org-capture-plist :key))) (defun +org-capture-here () "Convenience command to insert a template at point" (interactive) (org-capture 0)) (defun +org-capture-property-drawer () "Hook function run durning `org-capture-mode-hook'. If a template has a :properties keyword, add them to the entry." (when (eq (org-capture-get :type 'local) 'entry) (when-let ((properties (doct-get :properties t))) (dolist (property properties) (org-set-property (symbol-name (car property)) (replace-regexp-in-string "\n.*" "" (org-capture-fill-template (doct--replace-template-strings (cadr property))))))))) (defun +org-capture-todo () "Set capture entry to TODO automatically" (org-todo "TODO")) #+end_src #+begin_src emacs-lisp :lexical t (setq org-capture-templates (doct `(("Appointment" :keys "a" :id "2cd2f75e-b600-4e9b-95eb-6baefeaa61ac" :properties ((Created "%U")) :template ("* %^{appointment} %^g" "%?") :hook (lambda () (+org-capture-property-drawer) (unless org-note-abort (+org-schedule-relative-to-deadline)))) ("Account" :keys "A" :properties ((Created "%U")) :template ("* TODO %^{description} %^g" "%?") :hook +org-capture-property-drawer :children (("Buy" :keys "b" :id "e1dcca6e-6d85-4c8e-b935-d50492b2cc58") ("Borrow" :keys "B" :id "a318b8ba-ed1a-4767-84bd-4f45eb409aab" :template ("* TODO Return %^{description} to %^{person} %^g" "DEADLINE: %^T" "%?")) ("Loan" :keys "l" :id "cfdd301d-c437-4aae-9738-da022eae8056" :template ("* TODO Get %^{item} back from %^{person} %^g" "DEADLINE: %^T" "%?")) ("Favor" :keys "f" :id "9cd02444-2465-4692-958b-f73edacd997f") ("Sell" :keys "s" :id "9c4a39c5-3ba6-4665-ac43-67e72f461c15"))) ("Bookmark" :keys "b" :hook +org-capture-property-drawer :id "7c20c705-80a3-4f5a-9181-2ea14a18fa75" :properties ((Created "%U")) :template ("* [[%x][%^{title}]] %^g" "%?")) ("Health" :keys "h" :children (("Blood Pressure" :keys "b" :type table-line :id "4d0c16dd-ce99-4e1b-bf9f-fb10802e48a1" :template "%(+compute-blood-pressure-table-row)|%?|" :table-line-pos "II-1"))) ("Listen" :keys "l" :hook (lambda () (+org-capture-property-drawer) (+org-capture-todo)) :template ("* TODO %^{Title} %^g" "%^{Genre}") :children (("Audio Book" :keys "a" :id "55a01ad5-24f5-40ec-947c-ed0bc507d4e8" :template "* TODO %^{Title} %^g %^{Author}p %^{Year}p %^{Genre}p") ("Music" :keys "m" :id "dc9cfb0f-c65b-4ebe-a082-e751bb3261a6" :template "%(wikinforg-capture \"album\")") ("Podcast" :keys "p" :id "881ee183-37aa-4e76-a5af-5be8446fc346" :properties ((URL "[[%^{URL}][%^{Description}]]"))) ("Radio" :keys "r" :id "78da1d3e-c83a-4769-9fb2-91e8ff7ab5da"))) ("Note" :keys "n" :file ,(defun +org-capture-repo-note-file () "Find note for current repository." (require 'projectile) (let* ((coding-system-for-write 'utf-8) ;;@MAYBE: extract this to a global variable. (notedir "~/Documents/devops/repo-notes/") (project-root (projectile-project-root)) (name (concat (file-name-base (directory-file-name project-root)) ".org")) (path (expand-file-name name (file-truename notedir)))) (with-current-buffer (find-file-noselect path) (unless (derived-mode-p 'org-mode) (org-mode) ;;set to utf-8 because we may be visiting raw file (setq buffer-file-coding-system 'utf-8-unix)) (when-let ((headline (doct-get :headline))) (unless (org-find-exact-headline-in-buffer headline) (goto-char (point-max)) (insert "* " headline) (org-set-tags (downcase headline)))) (unless (file-exists-p path) (write-file path)) path))) :template (lambda () (concat "* %{todo-state} " (when (y-or-n-p "Link? ") "%A\n") "%?")) :todo-state "TODO" :children (("bug" :keys "b" :headline "Bug") ("design" :keys "d" :headline "Design") ("documentation" :keys "D" :headline "Documentation") ("enhancement" :keys "e" :headline "Enhancement" :todo-state "IDEA") ("feature" :keys "f" :headline "Feature" :todo-state "IDEA") ("optimization" :keys "o" :headline "Optimization") ("miscellaneous" :keys "m" :headline "Miscellaneous") ("security" :keys "s" :headline "Security"))) ("Play" :keys "p" :id "be517275-3779-477f-93cb-ebfe0204b614" :hook +org-capture-todo :template "%(wikinforg-capture \"game\")") ("Read" :keys "r" :template "%(wikinforg-capture \"book\")" :hook +org-capture-todo :children (("fiction" :keys "f" :id "0be106fc-a920-4ab3-8585-77ce3fb793e8") ("non-fiction" :keys "n" :id "73c29c94-fb19-4012-ab33-f51158c0e59b"))) ("Say" :keys "s" :children (("word" :keys "w" :id "55e43a15-5523-49a6-b16c-b6fbae337f05" :template ("* %^{Word}" "%?")) ("Phrase" :keys "p" :id "c3dabe22-db69-423a-9737-f90bfc47238a" :template ("* %^{Phrase}" "%?")) ("Quote" :keys "q" :id "8825807d-9662-4d6c-a28f-6392d3c4dbe2" :template ("* %^{Quote}" "%^{Quotee}p")))) ("Todo" :keys "t" :id "0aeb95eb-25ee-44de-9ef5-2698514f6208" :hook (lambda () (+org-capture-property-drawer) ;;swallow org-todo quit so we don't abort the whole capture (condition-case nil (org-todo) (quit nil))) :properties ((Created "%U")) :template ("* %^{description} %^g" "%?")) ("use-package" :keys "u" :file ,(expand-file-name "init.org" user-emacs-directory) :function ,(defun +org-capture-use-package-form () "place point for use-package capture template." (org-fold-show-all) (goto-char (org-find-entry-with-id "f8affafe-3a4c-490c-a066-006aeb76f628")) (org-narrow-to-subtree) ;;popping off parent headline, evil and general.el since they are order dependent. (when-let* ((name (read-string "package name: ")) (headlines (nthcdr 4 (caddr (org-element-parse-buffer 'headline 'visible)))) (packages (mapcar (lambda (headline) (cons (plist-get (cadr headline) :raw-value) (plist-get (cadr headline) :contents-end))) headlines)) (target (let ((n (downcase name))) (cdr (cl-some (lambda (package) (and (string-greaterp n (downcase (car package))) package)) (nreverse packages)))))) ;;put name on template's doct plist (setq org-capture-plist (plist-put org-capture-plist :doct (plist-put (org-capture-get :doct) :use-package name))) (goto-char target) (org-end-of-subtree) (open-line 1) (forward-line 1))) :type plain :empty-lines-after 1 :template ("** %(doct-get :use-package)" "#+begin_quote" "%(read-string \"package description:\")" "#+end_quote" "#+begin_src emacs-lisp" "(use-package %(doct-get :use-package)%?)" "#+end_src")) ("Watch":keys "w" :template "%(wikinforg-capture \"%{entity}\")" :hook +org-capture-todo :children (("Film" :keys "f" :id "a730a2db-7033-40af-82c1-9b73528ab7d9" :entity "film") ("TV" :keys "t" :id "4a18a50e-909e-4d36-aa7a-b09e8c3b01f8" :entity "show") ("Presentation" :keys "p" :id "343fe4f4-867a-4033-b31a-8b57aba0345e" :template "* %^{Title} %^g %^{Year}p")))))) #+end_src =make-capture-frame= cobbled together from: - http://cestlaz.github.io/posts/using-emacs-24-capture-2/ - https://stackoverflow.com/questions/23517372/hook-or-advice-when-aborting-org-capture-before-template-selection Don't use this within Emacs. Rather, invoke it when connecting an Emacs client to a server with: #+begin_example sh emacsclient --create-frame \ --socket-name 'capture' \ --alternate-editor='' \ --frame-parameters='(quote (name . "capture"))' \ --no-wait \ --eval "(+org-capture-make-frame)" #+end_example #+begin_src emacs-lisp :lexical t (defun +org-capture-delete-frame (&rest _args) "Delete frame with a name frame-parameter set to \"capture\"" (when (and (daemonp) (string= (frame-parameter (selected-frame) 'name) "capture")) (delete-frame))) (add-hook 'org-capture-after-finalize-hook #'+org-capture-delete-frame 100) #+end_src #+begin_src emacs-lisp :lexical t (defun +org-capture-make-frame () "Create a new frame and run org-capture." (interactive) (select-frame-by-name "capture") (delete-other-windows) (cl-letf (((symbol-function 'switch-to-buffer-other-window) #'switch-to-buffer)) (condition-case err (org-capture) ;; "q" signals (error "Abort") in `org-capture' ;; delete the newly created frame in this scenario. (user-error (when (string= (cadr err) "Abort") (delete-frame)))))) #+end_src #+begin_src emacs-lisp :lexical t :commands (+org-capture-make-frame) :general (:states 'normal :keymaps 'org-capture-mode-map ",c" 'org-capture-finalize ",k" 'org-capture-kill ",r" 'org-capture-refile) #+end_src #+begin_src emacs-lisp :results silent :custom (org-capture-dir (concat (getenv "HOME") "/Documents/todo/"))) #+end_src ** org-chronicle #+begin_src emacs-lisp :lexical t :tangle no (use-package org-chronicle :ensure (:host github :repo "progfolio/org-chronicle") :commands (org-chronicle) :custom (org-chronicle-directory "~/Documents/journal/") (org-chronicle-file-format-string "%Y.%m.%d")) #+end_src ** org-clean-refile #+begin_src emacs-lisp :lexical t (use-package org-clean-refile :ensure (org-clean-refile :host github :repo "progfolio/org-clean-refile" :protocol ssh) :commands (org-clean-refile) :after (org) :general (global-leader :keymaps 'org-mode-map "sr" 'org-clean-refile)) #+end_src ** org-contrib #+begin_src emacs-lisp :lexical t :tangle no (use-package org-contrib) #+end_src ** org-fancy-priorities #+begin_quote A minor mode that displays org priorities as custom strings. https://github.com/harrybournis/org-fancy-priorities #+end_quote #+begin_src emacs-lisp :lexical t (use-package org-fancy-priorities :commands (org-fancy-priorities-mode) :hook (org-mode . org-fancy-priorities-mode) :config ;;"Eisenhower Matrix of Importance and Urgency" (defvar +org-fancy-priorities-eisenhower-matrix "↑ |-----------+-----------| I | Eisenhower Matrix | M |-----------+-----------| P | | | O | Schedule | Immediate | R | | | T |-----------+-----------| A | | | N | Eliminate | Delegate | C | | | E |-----------+-----------| URGENCY →" "Eisenhower Matrix help text.") (setq org-fancy-priorities-list (mapcar (lambda (cell) (format (car cell) (propertize (cdr cell) 'help-echo +org-fancy-priorities-eisenhower-matrix))) '(("I∧U (%s)" . "I") ("I¬U (%s)" . "S") ("¬IU (%s)" . "D") ("¬I¬U (%s)" . "E"))))) #+end_src ** org-habit #+begin_src emacs-lisp :lexical t (use-feature org-habit :after (org) :config (defun +org-habit-graph-on-own-line (graph) "Place org habit consitency graph below the habit." (let* ((count 0) icon) (save-excursion (beginning-of-line) (while (and (eq (char-after) ? ) (not (eolp))) (when (get-text-property (point) 'display) (setq icon t)) (setq count (1+ count)) (forward-char))) (add-text-properties (+ (line-beginning-position) count) (line-end-position) `(display ,(concat (unless icon " ") (string-trim-left (thing-at-point 'line)) (make-string (or org-habit-graph-column 0) ? ) (string-trim-right (propertize graph 'mouse-face 'inherit))))))) <> :custom <>) (integerp nil) #+end_src *** :custom :PROPERTIES: :header-args: :noweb-ref org-habit-custom :END: #+begin_src emacs-lisp :lexical t (org-habit-today-glyph #x1f4c5) (org-habit-completed-glyph #x2713) (org-habit-preceding-days 29) (org-habit-following-days 1) (org-habit-graph-column 3) (org-habit-show-habits-only-for-today nil) #+end_src *** habits on their own line :PROPERTIES: :header-args: :noweb-ref org-habit-graph-placement-advice :END: I've submitted a [[https://orgmode.org/list/87h7sx5f5z.fsf@gmail.com/T/#t][patch]] to customize consistency graph placement in the agenda. Rather than constantly rebase my patch on top of the latest Org, I'm adding advice to override the default placement. #+begin_src emacs-lisp :lexical t (defun +org-habit-insert-consistency-graphs (&optional line) "Insert consistency graph for any habitual tasks." (let ((inhibit-read-only t) (buffer-invisibility-spec '(org-link)) (moment (time-subtract nil (* 3600 org-extend-today-until)))) (save-excursion (goto-char (if line (line-beginning-position) (point-min))) (while (not (eobp)) (let ((habit (get-text-property (point) 'org-habit-p))) (when habit (let ((graph (org-habit-build-graph habit (time-subtract moment (days-to-time org-habit-preceding-days)) moment (time-add moment (days-to-time org-habit-following-days))))) (+org-habit-graph-on-own-line graph)))) (forward-line))))) (advice-add #'org-habit-insert-consistency-graphs :override #'+org-habit-insert-consistency-graphs) #+end_src ** org-indent #+begin_src emacs-lisp :lexical t (use-feature org-indent :after (org) :hook (org-mode . org-indent-mode) :config (define-advice org-indent-refresh-maybe (:around (fn &rest args) "when-buffer-visible") "Only refresh indentation when buffer's window is visible. Speeds up `org-agenda' remote operations." (when (get-buffer-window (current-buffer) t) (apply fn args)))) #+end_src *** refile This function allows me to refile within the currently open org files as well as agenda files. Useful for structural editing. Stolen from: [[https://emacs.stackexchange.com/questions/22128/how-to-org-refile-to-a-target-within-the-current-file?rq=1][stackoverflow: how to org-refile to a target within the current file?]] #+begin_src emacs-lisp :lexical t (defun +org-files-list () "Returns a list of the file names for currently open Org files" (delq nil (mapcar (lambda (buffer) (when-let* ((file-name (buffer-file-name buffer)) (directory (file-name-directory file-name))) (unless (string-suffix-p "archives/" directory) file-name))) (org-buffer-list 'files t)))) #+end_src #+begin_src emacs-lisp :lexical t (setq +org-max-refile-level 20) (setq org-outline-path-complete-in-steps nil org-refile-allow-creating-parent-nodes 'confirm org-refile-use-outline-path 'file org-refile-targets `((org-agenda-files :maxlevel . ,+org-max-refile-level) (+org-files-list :maxlevel . ,+org-max-refile-level))) #+end_src *** settings #+begin_src emacs-lisp :lexical t (setq org-agenda-files '("~/Documents/todo") org-agenda-text-search-extra-files '(agenda-archives) org-fold-catch-invisible-edits 'show-and-error org-confirm-babel-evaluate nil org-enforce-todo-dependencies t org-hide-emphasis-markers t org-hierarchical-todo-statistics nil org-log-done 'time org-log-reschedule t org-return-follows-link t org-reverse-note-order t org-src-tab-acts-natively t org-file-apps '((auto-mode . emacs) ("\\.mm\\'" . default) ("\\.mp[[:digit:]]\\'" . "/usr/bin/mpv --force-window=yes %s") ;;("\\.x?html?\\'" . "/usr/bin/firefox-beta %s") ("\\.x?html?\\'" . "/usr/bin/bash -c '$BROWSER %s'") ("\\.pdf\\'" . default))) #+end_src Set clock report duration format to floating point hours #+begin_src emacs-lisp :lexical t ;;(setq org-duration-format '(h:mm)) (setq org-duration-format '(("h" . nil) (special . 2))) #+end_src *** 'TODO' Keywords #+begin_src emacs-lisp :lexical t #+end_src ** org-make-toc #+begin_quote This package makes it easy to have one or more customizable tables of contents in Org files. They can be updated manually, or automatically when the file is saved. Links to headings are created compatible with GitHub’s Org renderer. https://github.com/alphapapa/org-make-toc #+end_quote #+begin_src emacs-lisp :lexical t (use-package org-make-toc :commands (org-make-toc)) #+end_src ** org-mime #+begin_quote org-mime can be used to send HTML email using Org-mode HTML export. https://github.com/org-mime/org-mime #+end_quote #+begin_src emacs-lisp :lexical t (use-package org-mime :after (org) :commands (org-mime-htmlize org-mime-org-buffer-htmlize org-mime-org-subtree-htmlize) :config (setq org-mime-export-options '( :with-latex dvipng :section-numbers nil :with-author nil :with-toc nil))) #+end_src ** org-modern #+begin_quote "Modern looks for Org" https://github.com/minad/org-modern #+end_quote #+begin_src emacs-lisp :lexical t (use-package org-modern :after (org) :config (global-org-modern-mode) (remove-hook 'org-agenda-finalize-hook 'org-modern-agenda)) #+end_src ** org-region-link #+begin_src emacs-lisp :lexical t (use-package org-region-link :ensure (org-region-link :host github :repo "progfolio/org-region-link" :protocol ssh) :after (org)) #+end_src ** org-roam #+begin_src emacs-lisp :lexical t (use-package org-roam :ensure (org-roam :host github :repo "org-roam/org-roam") :disabled t :general (+general-global-application "or" '(:ignore t :which-key "org-roam-setup")) :init (setq org-roam-v2-ack t)) #+end_src ** org-relativity #+begin_src emacs-lisp :lexical t :tangle no (use-package org-relativity :after (org) :ensure (:host github :repo "progfolio/org-relativity")) #+end_src ** org-superstar #+begin_quote Prettify headings and plain lists in Org mode. This package is a direct descendant of ‘org-bullets’ https://github.com/integral-dw/org-superstar-mode #+end_quote #+begin_src emacs-lisp :lexical t (use-package org-superstar :ensure (org-superstar :host github :repo "integral-dw/org-superstar-mode") :after (org)) #+end_src ** ox-gfm #+begin_quote Github flavored Markdown back-end for Org export engine https://github.com/larstvei/ox-gfm #+end_quote #+begin_src emacs-lisp :lexical t (use-package ox-gfm :defer t) #+end_src ** ox-twbs #+begin_quote Export org-mode docs as HTML compatible with Twitter Bootstrap. https://github.com/marsmining/ox-twbs #+end_quote #+begin_src emacs-lisp :lexical t (use-package ox-twbs :disabled t :after (org) :defer t) #+end_src *** TODO add export-define-derived-backend ** package-lint #+begin_quote This library provides a linter for the metadata in Emacs Lisp files which are intended to be packages. You can integrate it into your build process. https://github.com/purcell/package-lint #+end_quote #+begin_src emacs-lisp :lexical t (use-package package-lint :defer t :commands (package-lint-current-buffer +package-lint-elpaca) :config <<+package-lint-elpaca>>) #+end_src *** +package-lint-elpaca :PROPERTIES: :header-args: :noweb-ref +package-lint-elpaca :END: package-lint assumes package.el is the package manager. I use elpaca.el, so I get spurious warnings about uninstallable packages. This workaround creates a temporary package archive and enables package.el to appease package-lint. #+begin_src emacs-lisp :lexical t (defun +package-lint-elpaca () "Help package-lint deal with elpaca." (interactive) (require 'package) (setq package-user-dir "/tmp/elpa") (add-to-list 'package-archives '("melpa" . "https://melpa.org/packages/") t) (package-initialize) (package-refresh-contents)) (+package-lint-elpaca) #+end_src ** paren I want to have matching delimiters highlighted when point is on them so that I can make sure they're balanced easily. #+begin_src emacs-lisp :lexical t (use-feature paren :defer 1 :config (show-paren-mode)) #+end_src ** projectile #+begin_quote Projectile is a project interaction library for Emacs. Its goal is to provide a nice set of features operating on a project level without introducing external dependencies (when feasible). https://github.com/bbatsov/projectile #+end_quote #+begin_src emacs-lisp :lexical t (use-package projectile :after (general) :general (+general-global-project "!" 'projectile-run-shell-command-in-root "%" 'projectile-replace-regexp "&" 'projectile-run-async-shell-command-in-root "A" 'projectile-toggle-between-implementation-and-test "bn" 'projectile-next-project-buffer "bp" 'projectile-previous-project-buffer "c" 'projectile-compile-project "D" 'projectile-dired "e" 'projectile-edit-dir-locals "g" 'projectile-find-tag "G" 'projectile-regenerate-tags "I" 'projectile-invalidate-cache "k" 'projectile-kill-buffers "R" 'projectile-replace "s" 'projectile-save-project-buffers "T" 'projectile-test-project "v" 'projectile-vc) :config (add-to-list 'projectile-globally-ignored-directories "*node_modules") (projectile-mode)) #+end_src ** pdf-tools #+begin_quote PDF Tools is, among other things, a replacement of DocView for PDF files. The key difference is that pages are not pre-rendered by e.g. ghostscript and stored in the file-system, but rather created on-demand and stored in memory. https://github.com/politza/pdf-tools#about-this-package #+end_quote This allows me to customize the color of the generated pdf. #+begin_src emacs-lisp :lexical t (use-package pdf-tools :ensure (pdf-tools :pre-build ("./server/autobuild") :files (:defaults "server/epdfinfo")) :functions (pdf-isearch-batch-mode) :commands (pdf-tools-install pdf-view-mode) :custom (pdf-view-midnight-colors '("#AFA27C" . "#0F0E16")) :config (add-hook 'pdf-view-mode-hook (lambda () ;; get rid of borders on pdf's edges (set (make-local-variable 'evil-normal-state-cursor) (list nil)) ;;for fast i-search in pdf buffers (pdf-isearch-minor-mode) (pdf-isearch-batch-mode) (pdf-view-dark-minor-mode) (pdf-view-midnight-minor-mode))) :mode (("\\.pdf\\'" . pdf-view-mode))) #+end_src ** rainbow-mode #+begin_quote Colorize color names in buffers https://elpa.gnu.org/packages/rainbow-mode.html #+end_quote #+begin_src emacs-lisp :lexical t (use-package rainbow-mode :commands (rainbow-mode)) #+end_src ** re-builder (regular expressions) Emacs has a horrible regexp syntax. A tool called re-builder allows you to live preview regular expressions. This variable reduces some of the escaping necessary when building regular expressions. #+begin_src emacs-lisp :lexical t (use-feature re-builder :custom (reb-re-syntax 'rx) :commands (re-builder)) #+end_src ** recentf I want to have more recent files saved. This was originally set to ten, but opening my org agenda files wipes that list out. #+begin_src emacs-lisp :lexical t (use-feature recentf :defer 1 :config (recentf-mode) :custom (recentf-max-menu-items 1000 "Offer more recent files in menu") (recentf-max-saved-items 1000 "Save more recent files")) #+end_src ** sendmail #+begin_src emacs-lisp :lexical t (use-feature sendmail :defer t :custom (send-mail-function 'smtpmail-send-it "inform emacs-bug-report how we want to send mail")) #+end_src ** shr-color #+begin_src emacs-lisp :lexical t (use-feature shr-color :custom (shr-color-visible-luminance-min 85 "For clearer email/eww rendering of bg/fg colors") (shr-use-colors nil "Don't use colors (for HTML email legibility)")) #+end_src ** smtpmail #+begin_src emacs-lisp :lexical t (use-feature smtpmail :defer t :custom (smtpmail-queue-mail nil) (smtpmail-servers-requiring-authorization "purelymail")) #+end_src ** straight.el #+begin_src emacs-lisp :lexical t (elpaca (straight.el :host github :depth nil :repo "radian-software/straight.el" :branch "develop" :files ("straight*.el") :protocol ssh :remotes ("origin" ("fork" :repo "progfolio/straight.el")))) #+end_src ** tab-bar #+begin_src emacs-lisp :lexical t (use-feature tab-bar :custom (tab-bar-close-button-show nil "Dont' show the x button on tabs") (tab-bar-new-button-show nil) (tab-bar-show nil "hide the tab bar. Use commands to access tabs.") :general (+general-global-tab "b" 'tab-bar-history-back "d" 'tab-bar-close-tab "f" 'tab-bar-history-forward "N" 'tab-bar-new-tab "n" 'tab-bar-switch-to-next-tab "p" 'tab-bar-switch-to-prev-tab "L" '((lambda (arg) (interactive "p") (tab-bar-move-tab arg)) :which-key "tab-bar-move-tab-right") "l" 'tab-bar-switch-to-next-tab "H" '((lambda (arg) (interactive "p") (tab-bar-move-tab (- arg))) :which-key "tab-bar-move-tab-left") "h" 'tab-bar-switch-to-prev-tab "r" 'tab-bar-rename-tab "t" 'tab-bar-switch-to-tab "u" 'tab-bar-undo-close-tab "O" 'tab-bar-close-other-tabs "w" 'tab-bar-move-tab-to-frame)) #+end_src ** tab-line #+begin_src emacs-lisp :lexical t (use-feature tab-line :custom (tab-line-close-button-show nil) (tab-line-new-button-show nil)) #+end_src ** TODO tern (build fails) #+begin_quote Tern is a stand-alone, editor-independent JavaScript analyzer that can be used to improve the JavaScript integration of existing editors. https://github.com/ternjs/tern #+end_quote #+begin_src emacs-lisp :lexical t (use-package tern :disabled t :commands (tern-mode) :hook (js2-mode . tern-mode)) #+end_src ** joymacs #+begin_src emacs-lisp :lexical t (use-package joymacs :ensure (joymacs :host github :repo "skeeto/joymacs" :main "joydemo.el" :pre-build ("make") :files (:defaults "joymacs.c" "joymacs.so") :remotes (("fork" :repo "progfolio/joymacs") "origin")) :defer t) #+end_src ** padscape #+begin_src emacs-lisp :lexical t (use-package padscape :ensure (padscape :host github :repo "progfolio/padscape" :protocol ssh) :commands (padscape) :config (add-to-list 'display-buffer-alist '("\\*padscape\\*" display-buffer-below-selected (window-height . 0.20)))) #+end_src ** speedo #+begin_src emacs-lisp :lexical t (use-package speedo :ensure (speedo :host github :repo "progfolio/speedo" :depth nil :protocol ssh :files (:defaults "test")) :commands (speedo speedo-load-file akogare) :custom (speedo-directory "~/.emacs.d/elpaca/repos/speedo/test/") (speedo-default-splits-file "./Akogare-Mario-World.spd") (speedo-confirm-evaluate nil) :general (+general-global-application "s" '(:ignore t :which-key "speedo") "ss" 'speedo "sl" 'speedo-load-file) :config (defun akogare () (interactive) (speedo) (tab-bar-mode -1) (padscape)) (evil-make-intercept-map speedo-mode-map) (defvar +speedo-debounce-interval 0.5) (defvar +speedo-debounce-timer nil) (defun +speedo-debounced-mistake (advised) "Execute `speedo-mistake' after last key press. This allows me to spam the button to get through the retry screen as fast as possible without recording multiple mistakes." (when +speedo-debounce-timer (cancel-timer +speedo-debounce-timer)) (setq +speedo-debounce-timer (run-at-time +speedo-debounce-interval nil advised))) (advice-add 'speedo-mistake :around #'+speedo-debounced-mistake) (defun +speedo-ensure-controller-support () "Launch controller program if it isn't already running." (interactive) (when (string-empty-p (shell-command-to-string "pgrep antimicrox")) (make-process :name "antimicrox" :noquery t ; Don't ask to kill this process when exiting Emacs. :buffer "*antimicrox*" :command '("antimicrox" "--tray")))) (add-hook 'speedo-mode-hook #'+speedo-ensure-controller-support)) (use-feature speedo-review :defer t :general (+general-global-application "sr" '(:ignore t :which-key "review") "srr" 'speedo-review "srt" 'speedo-review-top-runs "srl" '(:ignore t :which-key "review-last") "srlr" 'speedo-review-last-runs "srla" 'speedo-review-last-attempts) :config (evil-make-intercept-map speedo-review-mode-map)) (use-feature speedo-edit :defer t :config (evil-make-intercept-map speedo-edit-mode-map)) #+end_src ** time I like to see the date and time in my mode line. I use doom-modeline for the rest of my mode line configuration. #+begin_src emacs-lisp :lexical t (use-feature time :custom (display-time-day-and-date t "Show date, day, and time") :config (display-time)) #+end_src ** tramp #+begin_src emacs-lisp :lexical t (use-feature tramp :defer t :custom (tramp-terminal-type "tramp") :config (setq debug-ignored-errors (cons 'remote-file-error debug-ignored-errors))) #+end_src ** typst-ts-mode #+begin_src emacs-lisp :lexical t (use-package typst-ts-mode :ensure (:host codeberg :repo "meow_king/typst-ts-mode") :custom (typst-ts-mode-watch-options "--open") (typst-ts-mode-enable-raw-blocks-highlight t) (typst-ts-mode-highlight-raw-blocks-at-startup t)) #+end_src ** vc-hooks You probably want this 99% of the time and it will skip an annoying prompt. #+begin_src emacs-lisp :lexical t (use-feature vc-hooks :custom (vc-follow-symlinks t "Visit real file when editing a symlink without prompting.")) #+end_src ** vertico #+begin_quote VERTical Interactive COmpletion #+end_quote #+begin_src emacs-lisp :lexical t (use-package vertico :demand t :custom (vertico-cycle t) :config (setf (car vertico-multiline) "\n") ;; don't replace newlines (vertico-mode) (define-key vertico-map (kbd "C-h") #'+minibuffer-up-dir)) #+end_src ** vterm #+begin_quote Emacs-libvterm (vterm) is fully-fledged terminal emulator inside GNU Emacs based on libvterm, a C library. https://github.com/akermu/emacs-libvterm #+end_quote #+begin_src emacs-lisp :lexical t (use-package vterm :ensure (vterm :post-build (progn (setq vterm-always-compile-module t) (require 'vterm) ;;print compilation info for elpaca (with-current-buffer (get-buffer-create vterm-install-buffer-name) (goto-char (point-min)) (while (not (eobp)) (message "%S" (buffer-substring (line-beginning-position) (line-end-position))) (forward-line))) (when-let ((so (expand-file-name "./vterm-module.so")) ((file-exists-p so))) (make-symbolic-link so (expand-file-name (file-name-nondirectory so) "../../builds/vterm") 'ok-if-already-exists)))) :commands (vterm vterm-other-window) :general (+general-global-application "t" '(:ignore t :which-key "terminal") "tt" 'vterm-other-window "t." 'vterm) :config (evil-set-initial-state 'vterm-mode 'emacs)) #+end_src ** which-key #+begin_quote which-key is a minor mode for Emacs that displays the key bindings following your currently entered incomplete command (a prefix) in a popup. https://github.com/justbur/emacs-which-key #+end_quote #+begin_src emacs-lisp :lexical t (use-package which-key :demand t :init (setq which-key-enable-extended-define-key t) :config (which-key-mode) :custom (which-key-side-window-location 'bottom) (which-key-sort-order 'which-key-key-order-alpha) (which-key-side-window-max-width 0.33) (which-key-idle-delay 0.2)) #+end_src ** wikinforg #+begin_src emacs-lisp :lexical t (use-package wikinfo :ensure (wikinfo :host github :repo "progfolio/wikinfo" :branch "develop" :protocol ssh :depth nil) :defer t) #+end_src #+begin_src emacs-lisp :lexical t (use-package wikinforg :ensure (wikinforg :host github :repo "progfolio/wikinforg" :protocol ssh :depth nil) :commands (wikinforg wikinforg-capture) :custom (wikinforg-include-thumbnail t) (wikinforg-post-insert-hook '(org-redisplay-inline-images)) (wikinforg-thumbnail-directory (expand-file-name "wikinforg" user-emacs-directory)) :config (add-hook 'wikinforg-mode-hook #'visual-line-mode) (add-hook 'wikinforg-mode-hook #'olivetti-mode) (add-hook 'wikinforg-mode-hook (lambda () (writegood-mode -1))) ;; So we can bury temp buffers without evil bindings taking precedence (evil-make-intercept-map wikinforg-mode-map)) #+end_src ** winner #+begin_src emacs-lisp :lexical t (use-feature winner :defer 5 :config (+general-global-window "u" 'winner-undo "r" 'winner-redo) (winner-mode)) #+end_src ** window #+begin_src emacs-lisp :lexical t (use-feature window :custom (switch-to-buffer-obey-display-actions t) (switch-to-prev-buffer-skip-regexp '("\\*Help\\*" "\\*Calendar\\*" "\\*mu4e-last-update\\*" "\\*Messages\\*" "\\*scratch\\*" "\\magit-.*"))) #+end_src ** wordel #+begin_src emacs-lisp :lexical t (use-package wordel :ensure (wordel :host github :repo "progfolio/wordel" :files (:defaults "words") :depth nil :protocol ssh) :defer t :config (evil-make-intercept-map wordel-mode-map) (evil-set-initial-state 'wordel-mode 'insert) (evil-make-intercept-map wordel-select-mode-map) (evil-set-initial-state 'wordel-select-mode 'insert)) #+end_src ** writegood #+begin_quote This is a minor mode to aid in finding common writing problems. https://github.com/bnbeckwith/writegood-mode #+end_quote #+begin_src emacs-lisp :lexical t (use-package writegood-mode :commands (writegood-mode) :hook (org-mode)) #+end_src ** yasnippet #+begin_quote YASnippet is a template system for Emacs. It allows you to type an abbreviation and automatically expand it into function templates. https://github.com/joaotavora/yasnippet #+end_quote #+begin_src emacs-lisp :lexical t (use-package yasnippet :commands (yas-global-mode) :custom (yas-snippet-dirs '("~/.emacs.d/snippets"))) #+end_src ** zerodark-theme #+begin_quote A dark theme for Emacs, inspired from Niflheim and One Dark https://github.com/NicolasPetton/zerodark-theme #+end_quote #+begin_src emacs-lisp :lexical t (use-package zerodark-theme :defer t) #+end_src ** yodel #+begin_src emacs-lisp :lexical t (use-package yodel :ensure (yodel :host github :repo "progfolio/yodel" :depth nil :protocol ssh) :defer t) #+end_src ** spiel #+begin_src emacs-lisp :lexical t (use-package spiel :ensure (spiel :host github :repo "progfolio/spiel" :depth nil :protocol ssh) :defer t :hook (spiel-mode . (lambda () (olivetti-mode 1))) :config (with-eval-after-load "evil" (evil-make-intercept-map spiel-mode-map) (when (fboundp 'evil-set-initial-state) (evil-set-initial-state 'spiel-mode 'insert)))) #+end_src ** greg #+begin_src emacs-lisp :lexical t :tangle yes (use-package greg :defer t :ensure (greg :host github :repo "progfolio/greg" :depth nil :protocol ssh :files (:defaults "assets" "objects"))) #+end_src ** redact #+begin_src emacs-lisp :lexical t (use-package redact :defer t :ensure (:host github :repo "progfolio/redact" :depth nil :protocol ssh)) #+end_src ** subp #+begin_src emacs-lisp :lexical t (use-package subp :defer t :ensure (:host github :repo "progfolio/subp")) #+end_src ** HELPA #+begin_src emacs-lisp :lexical t (use-package helpa :defer t :ensure (:host github :main nil :build (:not elpaca--generate-autoloads-async) :repo "progfolio/HELPA" :depth nil :protocol ssh)) #+end_src ** tunic #+begin_src emacs-lisp :lexical t (use-package tunic :ensure (:host github :repo "~/elisp/tunic/") :defer t :config (evil-make-intercept-map tunic-mode-map) (evil-make-intercept-map tunic-decoder-mode-map)) #+end_src ** mel #+begin_src emacs-lisp :lexical t (use-package mel :defer t :ensure (:host github :repo "progfolio/mel" :protocol ssh)) #+end_src ** melpaca #+begin_src emacs-lisp :lexical t (use-package melpaca :defer t :ensure (:host github :repo "progfolio/melpaca" :protocol ssh)) #+end_src ** imp #+begin_src emacs-lisp :lexical t (use-package imp :defer t :ensure (:host github :repo "progfolio/imp" :protocol ssh)) #+end_src ** c4 #+begin_src emacs-lisp :lexical t (use-package c4 :defer t :ensure (:host github :repo "progfolio/c4" :protocol ssh)) #+end_src