+++ /dev/null
-#+TITLE: Emacs literate config\r
-#+PROPERTY: header-args :comments org :tangle yes\r
-\r
-* Header\r
-Set up the correct lexical binding to avoid screaming compiler\r
-#+BEGIN_SRC emacs-lisp :comments no\r
-;;; config.el --- Emacs Configuration -*- lexical-binding: t; -*-\r
-#+END_SRC\r
-* OS specific stuff\r
-#+BEGIN_SRC emacs-lisp\r
-(setq w32-recognize-altgr 'nil)\r
-\r
-(unless (equal system-type 'windows-nt)\r
- (setq trash-directory "~/Trash/")\r
- (defun doas ()\r
- "Use TRAMP to reopen the current buffer as root using doas."\r
- (interactive)\r
- (when buffer-file-name\r
- (find-alternate-file (concat "/doas:root@localhost:" buffer-file-name))))\r
- (defun sudo ()\r
- "Use TRAMP to reopen the current buffer as root using sudo."\r
- (interactive)\r
- (when buffer-file-name\r
- (find-alternate-file (concat "/sudo:root@localhost:" buffer-file-name)))))\r
-#+END_SRC\r
-* Packages\r
-** Add melpa, initialize\r
-#+BEGIN_SRC emacs-lisp\r
-(require 'package)\r
-\r
-(add-to-list 'package-archives '("melpa" . "https://melpa.org/packages/") t)\r
-(package-initialize)\r
-#+END_SRC\r
-** Install all packages that are not included\r
-#+BEGIN_SRC emacs-lisp\r
-(defvar *re/packages*\r
- '(corfu\r
- expand-region\r
- page-break-lines\r
- pdf-tools\r
- auctex\r
- csv-mode\r
- markdown-mode\r
- edit-indirect\r
- sly\r
- geiser\r
- geiser-chicken\r
- forth-mode\r
- trashed\r
- org-transclusion\r
- vc-jj\r
- minions))\r
-\r
-(dolist (pkg *re/packages*)\r
- (unless (package-installed-p pkg)\r
- (package-refresh-contents)\r
- (package-install pkg)))\r
-#+END_SRC\r
-** Corfu\r
-#+BEGIN_SRC emacs-lisp\r
-(require 'corfu)\r
-\r
-(setq corfu-auto t\r
- corfu-auto-delay 0\r
- corfu-auto-prefix 1)\r
-\r
-(add-hook 'corfu-mode-hook #'corfu-popupinfo-mode)\r
-\r
-(setq corfu-popupinfo-delay '(1.0 . 0.01) ; start after one second, fast refresh once on\r
- corfu-popupinfo-hide nil)\r
-\r
-(global-corfu-mode)\r
-#+END_SRC\r
-** Expand Region\r
-#+BEGIN_SRC emacs-lisp\r
-(require 'expand-region)\r
-\r
-(keymap-global-set "C-=" #'er/expand-region)\r
-#+END_SRC\r
-** Page Break Lines\r
-#+BEGIN_SRC emacs-lisp\r
-(require 'page-break-lines)\r
-(global-page-break-lines-mode)\r
-#+END_SRC\r
-** PDFtools\r
-Be sure to run =pdf-tools-install= before first run\r
-#+BEGIN_SRC emacs-lisp\r
-(add-to-list 'auto-mode-alist '("\\.pdf\\'" . pdf-tools-install))\r
-\r
-(setq-default pdf-view-display-size 'fit-page)\r
-#+END_SRC\r
-** Auctex\r
-#+BEGIN_SRC emacs-lisp\r
-(setq-default TeX-master nil)\r
-\r
-(setq TeX-auto-save t\r
- TeX-parse-self t\r
- TeX-PDF-mode t\r
- TeX-view-program-selection '((output-pdf "PDF Tools"))\r
- TeX-source-correlate-start-server t)\r
-\r
-(add-hook 'TeX-after-compilation-finished-functions\r
- #'TeX-revert-document-buffer)\r
-#+END_SRC\r
-** CSV Mode\r
-#+BEGIN_SRC emacs-lisp\r
-(add-hook 'csv-mode-hook 'csv-align-mode)\r
-#+END_SRC\r
-** Markdown Mode\r
-Make markdown act more like org when I'm forced to use it\r
-#+BEGIN_SRC emacs-lisp\r
-(setq markdown-special-ctrl-a/e t)\r
-\r
-(with-eval-after-load 'markdown-mode\r
- (keymap-set markdown-mode-map "M-<right>" #'markdown-demote)\r
- (keymap-set markdown-mode-map "M-<left>" #'markdown-promote)\r
- (keymap-set markdown-mode-map "M-<up>" #'markdown-move-up)\r
- (keymap-set markdown-mode-map "M-<down>" #'markdown-move-down)\r
- (keymap-set markdown-mode-map "C-M-u" #'markdown-up-heading)\r
- (keymap-set markdown-mode-map "C-M-b" #'markdown-outline-previous-same-level)\r
- (keymap-set markdown-mode-map "C-M-f" #'markdown-outline-next-same-level)\r
- (keymap-set markdown-mode-map "C-M-p" #'markdown-outline-previous)\r
- (keymap-set markdown-mode-map "C-M-n" #'markdown-outline-next))\r
-#+END_SRC\r
-** Sly\r
-#+BEGIN_SRC emacs-lisp\r
-(defun re/sly-repl-new-frame (&optional arg)\r
- "Start a SLY repl in a new frame."\r
- (interactive "P")\r
- (re/open-with-new-frame "SLY" #'sly))\r
-\r
-(defun re/sly-quit-current-lisp ()\r
- "Quit the current lisp repl connection without prompting."\r
- (interactive)\r
- (sly-quit-lisp nil nil))\r
-\r
-(defun re/sly-connect-stump (&optional arg)\r
- "Connect to my typical stumpwm instance."\r
- (interactive "P")\r
- (sly-connect "localhost" 4004))\r
-\r
-(setq inferior-lisp-program "sbcl"\r
- sly-net-coding-system 'utf-8-unix\r
- sly-command-switch-to-existing-lisp 'always)\r
-\r
-(let ((core (expand-file-name "~/sbcl.core-for-sly")))\r
- (when (file-exists-p core)\r
- (setq sly-lisp-implementations (list `(sbcl ("sbcl" "--core" ,core))))))\r
-\r
-(with-eval-after-load 'sly-mrepl\r
- (keymap-set sly-mrepl-mode-map ")" (re/send-on-close-paren #'sly-mrepl-return))\r
- (keymap-set sly-mrepl-mode-map "]" (re/balance-and-eval-sexp #'sly-mrepl-return))\r
- (keymap-set sly-mrepl-mode-map "C-c q" #'re/sly-quit-current-lisp))\r
-#+END_SRC\r
-*** Eldoc settings\r
-Sly really doesn't play well with elodc in the minibuffer, so here are some settings to make it better.\r
-#+BEGIN_SRC emacs-lisp\r
-(add-to-list 'display-buffer-alist\r
- '("^\\*eldoc" display-buffer-in-direction\r
- (direction . 'down)\r
- (window-height . 4)))\r
-(keymap-global-set "C-c d" #'eldoc) ; force eldoc to show in minibuffer\r
-(keymap-global-set "C-c D" #'eldoc-doc-buffer) ; open eldoc buffer when it's getting spammed\r
-#+END_SRC\r
-** Geiser\r
-#+BEGIN_SRC emacs-lisp\r
-(if (equal system-type 'windows-nt)\r
- (setq geiser-chicken-binary '("C:/tools/chicken/bin/csi.exe" "-:c"))\r
- (setq geiser-chicken-binary "csi -:c"))\r
-\r
-(setq scheme-program-name geiser-chicken-binary\r
- geiser-active-implementations '(chicken))\r
-\r
-(add-hook 'scheme-mode-hook 'geiser-mode)\r
-\r
-(with-eval-after-load 'geiser-repl\r
- (keymap-set geiser-repl-mode-map ")" (re/send-on-close-paren #'geiser-repl-maybe-send))\r
- (keymap-set geiser-repl-mode-map "]" (re/balance-and-eval-sexp #'geiser-repl-maybe-send)))\r
-#+END_SRC\r
-** ORG\r
-*** Set bindings, things that have to happen after loading\r
-#+BEGIN_SRC emacs-lisp\r
-(with-eval-after-load 'org\r
- (require 'org-mouse)\r
- (add-to-list 'org-export-backends 'md)\r
- (keymap-set org-mode-map "C-M-h" #'org-mark-subtree)\r
- (keymap-set org-mode-map "C-M-u" #'org-up-element)\r
- (keymap-set org-mode-map "C-M-d" #'org-down-element)\r
- (keymap-set org-mode-map "C-M-b" #'org-backward-heading-same-level)\r
- (keymap-set org-mode-map "C-M-f" #'org-forward-heading-same-level)\r
- (keymap-set org-mode-map "C-M-p" #'org-previous-visible-heading)\r
- (keymap-set org-mode-map "C-M-n" #'org-next-visible-heading)\r
- (keymap-set org-mode-map "C-c t" #'org-transclusion-mode))\r
-\r
-(keymap-global-set "C-c l" #'org-store-link)\r
-(keymap-global-set "C-c a" #'org-agenda)\r
-(keymap-global-set "C-c c" #'org-capture)\r
-#+END_SRC\r
-*** General options\r
-#+BEGIN_SRC emacs-lisp\r
-(setq org-M-RET-may-split-line nil\r
- org-cycle-inline-images-display t\r
- org-cycle-separator-lines -1\r
- org-ellipsis "->"\r
- org-enforce-todo-checkbox-dependencies t\r
- org-enforce-todo-dependencies t\r
- org-goto-interface 'outline-path-completion\r
- org-outline-path-complete-in-steps nil\r
- org-src-fontify-natively nil\r
- org-return-follows-link t\r
- org-reverse-note-order t\r
- org-special-ctrl-a/e t\r
- org-special-ctrl-k t\r
- org-src-preserve-indentation t\r
- org-startup-folded 'fold\r
- org-tags-column 0\r
- org-yank-adjusted-subtrees t\r
- org-refile-use-outline-path 'file\r
- org-refile-allow-creating-parent-nodes 'confirm)\r
-#+END_SRC\r
-**** Testing without these setq options\r
-#+BEGIN_SRC emacs-lisp :tangle no\r
- org-fontify-done-headline nil\r
- org-goto-auto-isearch nil\r
- org-list-allow-alphabetical t\r
- org-src-window-setup 'current-window)\r
-#+END_SRC\r
-*** Time Clocking\r
-#+BEGIN_SRC emacs-lisp :tangle no\r
-(org-clock-persistence-insinuate)\r
-(setq org-clock-in-resume t\r
- org-clock-out-remove-zero-time-clocks t\r
- org-clock-persist t\r
- org-clock-report-include-clocking-task t\r
- org-time-stamp-rounding-minutes '(1 1))\r
-#+END_SRC\r
-*** Agenda and Capture\r
-**** Agenda settings\r
-#+BEGIN_SRC emacs-lisp\r
- (setq org-agenda-files (append '("~/org/")\r
- (file-expand-wildcards "~/projects/*/"))\r
- org-refile-targets '((nil :maxlevel . 9)\r
- (org-agenda-files :maxlevel . 9))\r
- org-todo-keywords '((sequence "TODO(t)" "WAITING(w@)" "|" "DONE(d)" "BELAYED(b@)" "GIVEN(g@)"))\r
- org-todo-keyword-faces '(("WAITING" :foreground "#e85400" :weight bold)) ; alert orange\r
- org-agenda-prefix-format '((agenda . " %?-12t%? s")\r
- (todo . " ")\r
- (tags . " ")\r
- (search . " "))\r
- org-agenda-start-on-weekday nil\r
- org-read-date-prefer-future 'time\r
- org-log-done 'time\r
- org-log-redeadline 'time\r
- org-log-reschedule 'time\r
- org-log-state-notes-insert-after-drawers t\r
- org-agenda-window-setup 'current-window)\r
-#+END_SRC\r
-***** Testing without these options\r
-#+BEGIN_SRC emacs-lisp :tangle no\r
- org-tag-alist '((:startgroup)\r
- ("call" . ?c)\r
- ("net" . ?n)\r
- (:endgroup)\r
- (:startgroup)\r
- ("home" . ?h)\r
- ("office" . ?o)\r
- ("errand" . ?e)\r
- ("vps" . ?v)\r
- (:endgroup))\r
- org-agenda-span 'day\r
- org-agenda-restore-windows-after-quit t\r
- org-agenda-time-grid '((daily today require-timed)\r
- (800 1000 1200 1400 1600 1800 2000)\r
- " -----"\r
- "")\r
- org-agenda-scheduled-leaders '("Booked: " "Booked %dd. ago: ")\r
- org-agenda-deadline-leaders '("Deadline: " "In %dd.: " "Died %dd. ago: ")\r
- org-agenda-start-with-log-mode t\r
- org-agenda-log-mode-items '(closed clock state))\r
-#+END_SRC\r
-**** Stuck Projects\r
-#+BEGIN_SRC emacs-lisp\r
-(setq org-stuck-projects '("CATEGORY=\"undertakings\"+LEVEL=1/-DONE-GIVEN-BELAYED" ("TODO" "WAITING") nil ""))\r
-#+END_SRC\r
-**** Custom Agendas\r
-#+BEGIN_SRC emacs-lisp\r
- (defvar *re/org-agenda-inbox*\r
- '(tags "CATEGORY=\"inbox\"" ((org-agenda-overriding-header ""))))\r
-\r
- (defvar *re/org-agenda-todo*\r
- '(tags-todo "-recurring/TODO" ((org-agenda-overriding-header ""))))\r
-\r
- (defvar *re/org-agenda-undertakings*\r
- '(tags "CATEGORY=\"undertakings\"+LEVEL=1" ((org-agenda-overriding-header ""))))\r
-\r
- (defvar *re/org-agenda-waiting*\r
- '(todo "WAITING" ((org-agenda-overriding-header ""))))\r
-\r
-(defvar *re/org-agenda-done*\r
- '(tags "-recurring+LEVEL=1/DONE|BELAYED|GIVEN"))\r
-\r
- (setq org-agenda-custom-commands `(("i" "Inbox" ,@*re/org-agenda-inbox*)\r
- ("n" "Next Actions" ,@*re/org-agenda-todo*)\r
- ("u" "Undertakings" ,@*re/org-agenda-undertakings*)\r
- ("w" "Waiting" ,@*re/org-agenda-waiting*)\r
- ("d" "Done" ,@*re/org-agenda-done*)))\r
-#+END_SRC\r
-**** Capture Templates\r
-#+BEGIN_SRC emacs-lisp\r
- (setq org-capture-templates '(("i" "Inbox" entry\r
- (file "inbox.org")\r
- "* %^{Description}\n%U\n%?"\r
- :prepend t)\r
- ("n" "Next Action" entry\r
- (file "next.org")\r
- "* TODO %^{Description}\nTaken: %T\n%?"\r
- :prepend t)\r
- ("u" "Undertaking" entry\r
- (file "undertakings.org")\r
- "* %^{Description} [/]\nTaken: %T\n%?"\r
- :prepend t)\r
- ("m" "Meeting" entry\r
- (file "meetings.org")\r
- "* %^{Description}\n%^{Scheduled:}T\n%?"\r
- :prepend t)\r
- ("p" "Phone Call" entry\r
- (file "inbox.org")\r
- "* %^{Description} :call:\n%T\n%?"\r
- :prepend t)\r
- ("t" "Note" entry\r
- (file "notes.org")\r
- "* %^{Description}\n%U\n%?"\r
- :prepend t)))\r
-#+END_SRC\r
-*** IDs\r
-#+BEGIN_SRC emacs-lisp\r
-(setq org-id-link-to-org-use-id 'create-if-interactive-and-no-custom-id\r
- org-id-method 'ts\r
- org-clone-delete-id t\r
- org-attach-id-to-path-function-list '(org-attach-id-ts-folder-format\r
- org-attach-id-uuid-folder-format))\r
-\r
-(defun re/org-save-all ()\r
- "Save all `org-agenda-files' without user confirmation."\r
- (interactive)\r
- (message "Saving all org-agenda-files buffers...")\r
- (save-some-buffers t\r
- (lambda ()\r
- (when (member (buffer-file-name) org-agenda-files) t)))\r
- (message "Saving all org-agenda-files buffers... done"))\r
-\r
-(advice-add 'org-refile :after (lambda (&rest _) (re/org-save-all)))\r
-#+END_SRC\r
-*** Archiving\r
-#+BEGIN_SRC emacs-lisp\r
-(setq org-archive-location (concat "~/org/old.org_keep::datetree/")\r
- org-agenda-text-search-extra-files '(agenda-archives))\r
-\r
-(add-to-list 'auto-mode-alist '("\\.org_keep\\'" . org-mode))\r
-#+END_SRC\r
-*** Converting markdown to org\r
-#+BEGIN_SRC emacs-lisp\r
-(defun re/markdown-to-org-region (start end)\r
- "Convert Markdown formatted text in region (START, END) to Org.\r
-This command requires that pandoc (man page `pandoc(1)') be\r
-installed."\r
- (interactive "r")\r
- (shell-command-on-region\r
- start end\r
- "pandoc -f markdown -t org --wrap=preserve" t t))\r
-#+END_SRC\r
-*** Converting org to markdown\r
-#+BEGIN_SRC emacs-lisp\r
-(defun re/org-to-markdown-region (start end)\r
- "Convert Org formatted text in region (START, END) to Markdown.\r
-This command requires that pandoc (man page `pandoc(1)') be\r
-installed."\r
- (interactive "r")\r
- (shell-command-on-region\r
- start end\r
- "pandoc -f org -t markdown --wrap=preserve" t t))\r
-#+END_SRC\r
-** Trashed\r
-Shows the trash file as a dired buffer\r
-#+BEGIN_SRC emacs-lisp\r
-(setq trashed-action-confirmer 'y-or-n-p\r
- trashed-use-header-line t\r
- trashed-sort-key '("Date deleted" . t)\r
- trashed-date-format "%Y-%m-%d %H:%M:%S")\r
-#+END_SRC\r
-** Use mu4e on my home pcs\r
-#+BEGIN_SRC emacs-lisp\r
-(when (eq system-type 'gnu/linux)\r
- (with-eval-after-load 'mu4e\r
- (setq mail-user-agent 'message-user-agent\r
- message-send-mail-function 'smtpmail-send-it\r
- message-citation-line-format "Quoth %f :\n"\r
- smtpmail-default-smtp-server "mail.earman.xyz"\r
- smtpmail-smtp-server "mail.earman.xyz"\r
- smtpmail-smtp-service 587\r
- smtpmail-stream-type 'starttls\r
- smtpmail-local-domain "earman.xyz"\r
- smtpmail-queue-mail nil\r
- smtpmail-queue-dir "~/mail/Outbox/cur"\r
- user-mail-address "iv@earman.xyz"\r
- user-full-name "Rush N. Earman IV"\r
- message-signature "Rush Earman IV, PE"\r
- message-kill-buffer-on-exit t\r
- mu4e-get-mail-command "mbsync -a"\r
- mu4e-update-interval (* 10 60)\r
- mu4e-change-filenames-when-moving t\r
- mu4e-use-fancy-chars t\r
- mu4e-attachment-dir "~/downloads"\r
- mu4e-compose-reply-to-address "iv@earman.xyz"\r
- mu4e-maildir "~/mail"\r
- mu4e-drafts-folder "/Drafts"\r
- mu4e-sent-folder "/Sent"\r
- mu4e-trash-folder "/Trash"\r
- mu4e-maildir-shortcuts '((:maildir "/Inbox" :key ?i)\r
- (:maildir "/Sent" :key ?s)\r
- (:maildir "/Trash" :key ?t)\r
- (:maildir "/Drafts" :key ?d)))))\r
-#+END_SRC\r
-* Load libraries\r
-Libraries Not from the package manager\r
-#+BEGIN_SRC emacs-lisp\r
-(dolist (path '("sedit-mouse/" "themes/"))\r
- (add-to-list 'load-path (concat user-emacs-directory path)))\r
-\r
-(dolist (file '(eng-paper-theme sedit-mouse))\r
- (require file))\r
-#+END_SRC\r
-* UNIX/vi-like bindings\r
-=C-h=, =C-w=, =C-u=, et. al to work as I expect (like acme). Some functions get re-bound when these take over a default.\r
-#+BEGIN_SRC emacs-lisp\r
-(defun re/open-line-below (n)\r
- "Creates a new empty line below the current line and moves to it."\r
- (interactive "*p")\r
- (end-of-line)\r
- (open-line n)\r
- (next-line 1)\r
- (indent-for-tab-command))\r
-\r
-(keymap-global-set "C-o" #'re/open-line-below)\r
-\r
-(defun re/open-line-above (n)\r
- "Creates a new empty line above the current line."\r
- (interactive "*p")\r
- (beginning-of-line)\r
- (open-line n)\r
- (indent-for-tab-command))\r
-\r
-(keymap-global-set "M-o" #'re/open-line-above)\r
-\r
-(defun re/kill-bword-or-region (&optional arg)\r
- "Kill region if active, otherwise kill back one word."\r
- (interactive "p")\r
- (if (use-region-p)\r
- (kill-region (region-beginning) (region-end))\r
- (backward-kill-word arg)))\r
-\r
-(keymap-global-set "C-w" #'re/kill-bword-or-region)\r
-(keymap-global-set "C-u" (lambda () (interactive) (kill-line 0))) ; kill-line backwards\r
-(keymap-global-set "C-S-U" #'universal-argument)\r
-(keymap-set key-translation-map "C-h" "<DEL>") ; Rather F1 for help, also C-S-H works\r
-(keymap-global-set "M-j" (lambda () (interactive) (join-line 0))) ; join-line other way\r
-(keymap-global-set "<home>" #'beginning-of-buffer)\r
-(keymap-global-set "<end>" #'end-of-buffer)\r
-#+END_SRC\r
-* Commenting and defun-level bindings\r
-#+BEGIN_SRC emacs-lisp\r
-(defun re/comment-dwim (&optional arg)\r
- "Comment region if active, otherwise comment line.\r
- Stolen from https://depp.brause.cc/dotemacs"\r
- (interactive "P")\r
- (if (use-region-p)\r
- (comment-or-uncomment-region (region-beginning) (region-end))\r
- (comment-line arg)))\r
-\r
-(keymap-global-set "M-;" #'re/comment-dwim)\r
-\r
-(defun re/comment-defun (&optional arg)\r
- "Mark a defun and comment it out.\r
- Return to initial position after. Arg works on `mark-defun'."\r
- (interactive "P")\r
- (save-excursion (mark-defun arg)\r
- (comment-region (region-beginning) (region-end))))\r
-\r
-(keymap-global-set "C-M-;" #'re/comment-defun)\r
-\r
-(defun re/kill-defun (&optional arg)\r
- "Mark a defun and kill it.\r
- Arg works on `mark-defun'."\r
- (interactive "P")\r
- (mark-defun arg)\r
- (kill-region (region-beginning) (region-end)))\r
-\r
-(keymap-global-set "C-M-S-K" #'re/kill-defun)\r
-#+END_SRC\r
-* Misc QOL bindings\r
-#+BEGIN_SRC emacs-lisp\r
-(keymap-global-set "<insert>" nil) ; I keep toggliĆ overwrite-mode haply\r
-(keymap-global-set "C-\\" #'undo-redo) ; issues with C-? sometimes\r
-(keymap-global-set "C-S-K" #'kill-whole-line)\r
-(keymap-global-set "C-S-d" #'delete-pair)\r
-(keymap-global-set "C-z" #'zap-up-to-char)\r
-(keymap-global-set "C-c s" #'sort-lines)\r
-(keymap-global-set "<remap> <capitalize-word>" #'capitalize-dwim) ; M-c\r
-(keymap-global-set "<remap> <downcase-word>" #'downcase-dwim) ; M-l\r
-(keymap-global-set "<remap> <upcase-word>" #'upcase-dwim) ; M-u\r
-#+END_SRC\r
-* Tabs\r
-I want *TABS*, not spaces, dammit. Globally overwrite the tab key to insert the tab character. This makes it so that many things (completion) will have to be triggered with =C-i= instead, but I don't think it's necessarily bad to have things like that on a control combo instead of overloading the tab key at the expense of being able to actually insert tabs.\r
-#+BEGIN_SRC emacs-lisp\r
-;; Tabs\r
-(setq-default indent-tabs-mode t\r
- tab-width 8\r
- backward-delete-char-untabify-method nil)\r
-\r
-(advice-add 'indent-to :around\r
- (lambda (orig-fun column &rest args)\r
- (when indent-tabs-mode\r
- (setq column (* tab-width (round column tab-width))))\r
- (apply orig-fun column args)))\r
-\r
-(dolist (indent-level '(c-basic-offset cperl-indent-level LaTeX-indent-level))\r
- (defvaralias indent-level 'tab-width))\r
-\r
-(defun Tab (width)\r
- "Give me a function analagous to the Acme `Tab' command"\r
- (interactive)\r
- (setq-local tab-width width))\r
-\r
-(keymap-global-set "<tab>" (lambda () (interactive) (insert "\t")))\r
-#+END_SRC\r
-* Buffer dealiĆ\r
-#+BEGIN_SRC emacs-lisp\r
-(setq-default indicate-empty-lines t\r
- truncate-lines t\r
- fill-column 78)\r
-\r
-(dolist (hook '(text-mode-hook LaTeX-mode-hook))\r
- (add-hook hook #'visual-line-mode))\r
-\r
-(blink-cursor-mode -1)\r
-(delete-selection-mode t)\r
-\r
-(setq delete-by-moving-to-trash t\r
- delete-pair-blink-delay 0\r
- use-short-answers t\r
- use-file-dialog nil\r
- echo-keystrokes 0.1\r
- eldoc-idle-delay 0\r
- read-buffer-completion-ignore-case t\r
- history-delete-duplicates t\r
- disabled-command-function nil\r
- save-interprogram-paste-before-kill t\r
- confirm-kill-processes nil)\r
-\r
-(keymap-global-set "C-x M-f" #'find-file-at-point)\r
-(keymap-global-set "C-x C-b" #'buffer-menu)\r
-(keymap-global-set "C-x M-b" #'buffer-menu-other-window)\r
-(keymap-global-set "C-x k" #'kill-current-buffer)\r
-(keymap-global-set "C-x K" #'kill-buffer)\r
-#+END_SRC\r
-* Recentering and page-by-page navigation\r
-#+BEGIN_SRC emacs-lisp\r
-(setq recenter-positions '(2 middle -3))\r
-\r
-(defun re/recenter-advice (&rest _)\r
- "Recenter to page start."\r
- (when (called-interactively-p 'any) (recenter 2)))\r
-\r
-(dolist (binding '(backward-page forward-page))\r
- (advice-add binding :after #'re/recenter-advice))\r
-\r
-(keymap-global-set "C-x C-n" #'forward-page)\r
-(keymap-global-set "C-x C-p" #'backward-page)\r
-(keymap-global-set "<next>" #'forward-page)\r
-(keymap-global-set "<prior>" #'backward-page)\r
-#+END_SRC\r
-* Macro to open something in its own named frame\r
-Start a repl in a new frame, open dired in a new frame, etc. etc.\r
-#+BEGIN_SRC emacs-lisp\r
-(defmacro re/open-with-new-frame (frname function)\r
- "Run the function in its own named frame."\r
- `(progn\r
- (select-frame (make-frame '((name . ,(eval frname)))))\r
- (call-interactively ,function)))\r
-#+END_SRC\r
-* Scratch buffer easy access\r
-#+BEGIN_SRC emacs-lisp\r
-(defun re/scratch-only ()\r
- "Bring up the scratch buffer as the only visible buffer."\r
- (interactive)\r
- (scratch-buffer)\r
- (delete-other-windows))\r
-\r
-(keymap-global-set "<f5>" #'scratch-buffer)\r
-(keymap-global-set "S-<f5>" #'re/scratch-only)\r
-#+END_SRC\r
-* Switch or split window\r
-Game changer. Auto choose how to split, or just switch to the next, all on the same binding. Ripped from an early version of the lem editor.\r
-#+BEGIN_SRC emacs-lisp\r
-(defun re/other-window-or-split-window (&optional window)\r
- "Go to other window or split sensibly."\r
- (interactive)\r
- (if (= (count-windows) 1)\r
- (funcall split-window-preferred-function window)\r
- (other-window 1)))\r
-\r
-(keymap-global-set "C-M-o" #'re/other-window-or-split-window)\r
-#+END_SRC\r
-* Smart End/Beginning of line\r
-Similar to what org does, and indeed calls the org function if in org-mode\r
-#+BEGIN_SRC emacs-lisp\r
-(defun re/smart-beginning-of-line ()\r
- "Move point to first non-whitespace character or `beginning-of-visual-line'.\r
-If point was already at that position, call `back-to-indentation'.\r
-Called a third time, move point back to `beginning-of-line'.\r
-If in org-mode, call `org-beginning-of-line' first."\r
- (interactive "^")\r
- (let ((oldpos (point)))\r
- (if (equal major-mode 'org-mode)\r
- (org-beginning-of-line)\r
- (progn\r
- (when visual-line-mode (beginning-of-visual-line 1))\r
- (skip-syntax-forward " " (line-end-position))\r
- (backward-prefix-chars)))\r
- (and (= oldpos (point))\r
- (let ((newpos (point)))\r
- (back-to-indentation)\r
- (and (= newpos (point)) (beginning-of-line))))))\r
-\r
-(keymap-global-set "<remap> <move-beginning-of-line>" #'re/smart-beginning-of-line)\r
-(keymap-set visual-line-mode-map "C-a" #'re/smart-beginning-of-line)\r
-\r
-(defun re/smart-end-of-line ()\r
- "Move point to the end of the visual line.\r
- If point was already at that position, call `move-end-of-line'.\r
-If in org-mode, call `org-end-of-line' first."\r
- (interactive "^")\r
- (let ((oldpos (point)))\r
- (if (equal major-mode 'org-mode)\r
- (org-end-of-line)\r
- (when visual-line-mode\r
- (end-of-visual-line 1)))\r
- (and (= oldpos (point)) (move-end-of-line nil))))\r
-\r
-(keymap-global-set "<remap> <move-end-of-line>" #'re/smart-end-of-line)\r
-(keymap-set visual-line-mode-map "C-e" #'re/smart-end-of-line)\r
-#+END_SRC\r
-* Goto line with numbers\r
-I only care about line numbers when I want to jump, so only show them then\r
-#+BEGIN_SRC emacs-lisp\r
-(defun re/goto-line ()\r
- "Show line numbers before going to line, then hide them again."\r
- (interactive)\r
- (display-line-numbers-mode 1)\r
- (call-interactively 'goto-line)\r
- (display-line-numbers-mode -1))\r
-\r
-(keymap-global-set "<remap> <goto-line>" #'re/goto-line)\r
-#+END_SRC\r
-* Switching between buffers\r
-#+BEGIN_SRC emacs-lisp\r
-(keymap-global-set "C-." #'next-buffer)\r
-(keymap-global-set "C-," #'previous-buffer)\r
-(keymap-global-set "C-<tab>" (lambda () (interactive) (switch-to-buffer nil))) ; toggle last two buffers\r
-(keymap-global-set "<mouse-4>" #'previous-buffer) ; Back button\r
-(keymap-global-set "<mouse-5>" #'next-buffer) ; Forward button\r
-#+END_SRC\r
-* Disable M-mouse for window manager bindings\r
-#+BEGIN_SRC emacs-lisp\r
-(keymap-global-unset "M-<down-mouse-1>")\r
-(keymap-global-unset "M-<drag-mouse-1>")\r
-(keymap-global-unset "M-<mouse-1>")\r
-(keymap-global-unset "M-<mouse-2>")\r
-(keymap-global-unset "M-<mouse-3>")\r
-#+END_SRC\r
-* Search\r
-#+BEGIN_SRC emacs-lisp\r
-;; Search\r
-(defun re/add-region-to-search-ring ()\r
- (when (use-region-p)\r
- (add-to-history 'search-ring\r
- (buffer-substring (region-beginning) (region-end)))\r
- (deactivate-mark)))\r
-\r
-(defun re/isearch-forward-use-region ()\r
- "Search text in region if active, otherwise normal forward search."\r
- (interactive)\r
- (re/add-region-to-search-ring)\r
- (isearch-forward))\r
-\r
-(keymap-global-set "C-s" #'re/isearch-forward-use-region)\r
-\r
-(defun re/isearch-backward-use-region ()\r
- "Search text in region if active, otherwise normal backward search."\r
- (interactive)\r
- (re/add-region-to-search-ring)\r
- (isearch-backward))\r
-\r
-(keymap-global-set "C-r" #'re/isearch-backward-use-region)\r
-\r
-(defun re/exchange-point-and-mark-maybe-activate (arg)\r
- "Call `exchange-point-and-mark' but don't activate the region.\r
-Stolen from https://www.masteringemacs.org/article/fixing-mark-commands-transient-mark-mode"\r
- (interactive "P")\r
- (exchange-point-and-mark (if (and transient-mark-mode (not mark-active))\r
- (not arg)\r
- arg)))\r
-\r
-(keymap-global-set "<remap> <exchange-point-and-mark>" #'re/exchange-point-and-mark-maybe-activate)\r
-\r
-(keymap-global-set "M-s ." #'isearch-forward-thing-at-point)\r
-(keymap-global-set "M-s M-." #'isearch-forward-symbol-at-point)\r
-(keymap-global-set "M-%" #'replace-regexp)\r
-(keymap-global-set "C-%" #'replace-string)\r
-(keymap-global-set "C-M-S-R" #'replace-regexp-as-diff)\r
-(keymap-global-set "C-S-S" #'isearch-forward-regexp)\r
-(keymap-global-set "C-S-R" #'isearch-backward-regexp)\r
-#+END_SRC\r
-* Recent Files/Buffers\r
-#+BEGIN_SRC emacs-lisp\r
-(midnight-mode t)\r
-(setq clean-buffer-list-delay-general 1)\r
-(dolist (never-re '("\\` \\*tramp/.*\\'"\r
- "\\` \\*eshell.*\\'"))\r
- (add-to-list 'clean-buffer-list-kill-never-regexps never-re))\r
-(save-place-mode 1)\r
-(recentf-mode 1)\r
-#+END_SRC\r
-* File Handling\r
-#+BEGIN_SRC emacs-lisp\r
-(defun re/rename-current-buffer-file nil\r
- "Renames the current buffer and the file it is visiting."\r
- (interactive)\r
- (let ((name (buffer-name))\r
- (filename (buffer-file-name)))\r
- (if (not (and filename (file-exists-p filename)))\r
- (error "Buffer '%s' is not visiting a file!" name)\r
- (let ((new-name (read-file-name "New name: " filename)))\r
- (if (get-buffer new-name)\r
- (error "A buffer named '%s' already exists!" new-name)\r
- (rename-file filename new-name 1)\r
- (rename-buffer new-name)\r
- (set-visited-file-name new-name)\r
- (set-buffer-modified-p nil)\r
- (message "File '%s' successfully renamed to '%s'"\r
- name\r
- (file-name-nondirectory new-name)))))))\r
-\r
-(keymap-global-set "C-x C-r" #'re/rename-current-buffer-file)\r
-\r
-(defun re/get-file-modified-time (filepath)\r
- (interactive)\r
- (format-time-string "%Y-%m-%d-%H%M"\r
- (file-attribute-modification-time (file-attributes filepath))))\r
-\r
-(defun re/filename-inject-mod-time (filepath)\r
- (interactive)\r
- (concat (file-name-sans-extension filepath)\r
- "_"\r
- (re/get-file-modified-time filepath)\r
- (file-name-extension filepath t)))\r
-\r
-(defun re/copy-with-mod-time (&optional filepath)\r
- (interactive)\r
- (let ((filepath (if (eq major-mode 'dired-mode)\r
- (dired-get-filename t)\r
- (when (null filepath)\r
- (read-file-name "File to copy with datestring: "\r
- default-directory\r
- nil\r
- t\r
- nil)))))\r
- (copy-file filepath (re/filename-inject-mod-time filepath ) nil t t t)))\r
-#+END_SRC\r
-* Dired\r
-#+BEGIN_SRC emacs-lisp\r
-(defun re/dired-display-direction ()\r
- "In dired mode, visit the file at the cursor in the right/below/left/above window."\r
- (interactive)\r
- (let* ((file-or-dir (dired-get-file-for-visit)) ;; get the file at cursor\r
- (buffer (find-file-noselect file-or-dir))) ;; load the file into a buffer\r
- (let ((window ;; figure out the window to use\r
- (cond ((get-buffer-window buffer (selected-frame)))\r
- ((window-in-direction 'right)) ;; try window in each direction\r
- ((window-in-direction 'below)) ;; and default to right\r
- ((window-in-direction 'left)) ;; if no window found.\r
- ((window-in-direction 'above))\r
- (t (split-window-sensibly (selected-window))))))\r
- (window--display-buffer buffer window 'window nil)\r
- window)))\r
-\r
-(defun re/dired-new-frame ()\r
- "Open dired in a new frame."\r
- (interactive)\r
- (re/open-with-new-frame "DIRED"\r
- #'(lambda () (interactive) (dired default-directory))))\r
-\r
-(keymap-global-set "<f8>" #'re/dired-new-frame)\r
-\r
-(defun re/dired-find-marked-files ()\r
- "Open all marked files from `dired'."\r
- (interactive)\r
- (mapc #'find-file (reverse (dired-get-marked-files))))\r
-\r
-(defun re/dired-sort ()\r
- "Change sort order of dired dir."\r
- (interactive)\r
- (let ((smenu '(("name" . "-Ahl")\r
- ("mdate" . "-Ahlt")\r
- ("adate" . "-Ahltu")\r
- ("size" . "-AhlS")\r
- ("dir" . "-Ahl --group-directories-first")))\r
- ssortBy)\r
- (setq ssortBy (completing-read "Sort by (default name):"\r
- smenu\r
- nil\r
- t\r
- nil\r
- nil\r
- (caar smenu)))\r
- (dired-sort-other (cdr (assoc ssortBy smenu)))))\r
-\r
-(defun re/open-file-external (file)\r
- "Open a file in an external program."\r
- (interactive)\r
- (let ((process-connection-type nil))\r
- (cond ((eq system-type 'windows-nt)\r
- (w32-shell-execute "open"\r
- (replace-regexp-in-string "/" "\\\\" file t t)))\r
- ((eq system-type 'gnu/linux)\r
- (start-process "" nil "xdg-open" file))\r
- (t (error "Unable to automatically open this file")))))\r
-\r
-(defun re/dired-open-externally ()\r
- "Open the file at point in an external program."\r
- (interactive)\r
- (re/open-file-external (dired-get-filename)))\r
-\r
-(defun re/dired-open-marked-externally ()\r
- "Open all marked files from `dired' in an external program."\r
- (interactive)\r
- (mapc #'re/open-file-external (reverse (dired-get-marked-files))))\r
-\r
-(setq dired-listing-switches "-Ahl"\r
- dired-vc-rename-file t\r
- dired-auto-revert-buffer t\r
- dired-hide-details-hide-symlink-targets nil\r
- dired-kill-when-opening-new-dired-buffer t)\r
-\r
-(add-hook 'dired-mode-hook\r
- (lambda ()\r
- (keymap-set dired-mode-map "o" #'re/dired-display-direction)\r
- (keymap-set dired-mode-map "z" #'dired-do-compress-to)\r
- (keymap-set dired-mode-map "c" #'dired-do-chmod)\r
- (keymap-set dired-mode-map "r" #'dired-do-rename)\r
- (keymap-set dired-mode-map "M" #'dired-do-rename)\r
- (keymap-set dired-mode-map "s" #'re/dired-sort)\r
- (keymap-set dired-mode-map "F" #'re/dired-find-marked-files)\r
- (keymap-set dired-mode-map "M-C" #'re/copy-with-mod-time)\r
- (keymap-set dired-mode-map "b" #'re/dired-open-externally)\r
- (keymap-set dired-mode-map "B" #'re/dired-open-marked-externally)\r
- (dired-hide-details-mode t)\r
- (hl-line-mode)))\r
-#+END_SRC\r
-* Bookmarks\r
-#+BEGIN_SRC emacs-lisp\r
-(setq bookmark-save-flag 1)\r
-(keymap-global-set "<f7>" #'recentf-open-files)\r
-(keymap-global-set "C-x r B" #'bookmark-jump-other-window)\r
-#+END_SRC\r
-* GUI\r
-#+BEGIN_SRC emacs-lisp\r
-(load-theme 'eng-paper t)\r
-\r
-(if (equal system-type 'android)\r
- (progn\r
- (setq touch-screen-display-keyboard t)\r
- (set-frame-parameter nil 'tool-bar-position 'bottom)\r
- (define-key key-translation-map\r
- (kbd "<volume-up>")\r
- (kbd "C-i"))\r
- (define-key key-translation-map\r
- (kbd "<volume-down>")\r
- 'event-apply-meta-modifier)\r
- (modifier-bar-mode 1)\r
- (tool-bar-mode 1))\r
- (progn\r
- (menu-bar-mode -1)\r
- (tool-bar-mode -1)))\r
-(set-scroll-bar-mode (if (eq system-type 'windows-nt) nil 'left))\r
-\r
-(setq frame-title-format "%b"\r
- mouse-prefer-closest-glyph t\r
- mouse-1-click-follows-link nil\r
- focus-follows-mouse t\r
- mouse-autoselect-window -0.25)\r
-\r
-(setq-default cursor-type 'hbar\r
- cursor-in-non-selected-windows nil)\r
-#+END_SRC\r
-** Modeline\r
-*** Minions\r
-Remove all minor modes from the mode-line, and show /all/ of them in the minor mode menu. Must come before the modeline customization to ensure =minions-mode-line-modes= is defined.\r
-#+BEGIN_SRC emacs-lisp\r
-(require 'minions)\r
-(minions-mode)\r
-\r
-(setq minions-mode-line-lighter "+")\r
-#+END_SRC\r
-*** Format\r
-#+BEGIN_SRC emacs-lisp\r
-(setq-default mode-line-format '(" "\r
- (:eval (propertize "%b"\r
- 'face\r
- (when (buffer-modified-p)\r
- 'mode-line-highlight)))\r
- " "\r
- mode-line-remote\r
- " "\r
- minions-mode-line-modes\r
- (vc-mode vc-mode)\r
- mode-line-format-right-align\r
- (:eval (number-to-string (line-number-at-pos (point-max))))\r
- "L/%Ib (%l,%C) "))\r
-#+END_SRC\r
-** Pretty Symbols\r
-#+BEGIN_SRC emacs-lisp\r
-(setq prettify-symbols-alist '(("lambda" . 955)\r
- ("delta" . 120517)\r
- ("epsilon" . 120518)\r
- ("->" . 8594)\r
- ("<=" . 8804)\r
- (">=" . 8805)))\r
-\r
-(global-prettify-symbols-mode t)\r
-#+END_SRC\r
-* Parens\r
-Must be set after loading any theme\r
-#+BEGIN_SRC emacs-lisp\r
-(setq show-paren-delay 0\r
- show-paren-style 'expression)\r
-\r
-(setq electric-pair-pairs '((?\" . ?\")\r
- (?\â . ?\â)\r
- (?\â . ?\â)\r
- (?\{ . ?\})\r
- (?\< . ?\>)))\r
-#+END_SRC\r
-* Help\r
-#+BEGIN_SRC emacs-lisp\r
-(setq apropos-do-all t)\r
-#+END_SRC\r
-* UTF-8\r
-#+BEGIN_SRC emacs-lisp\r
-(prefer-coding-system 'utf-8-unix)\r
-(set-language-environment 'utf-8)\r
-#+END_SRC\r
-* Cleanup on write\r
-#+BEGIN_SRC emacs-lisp\r
-(defun re/flush-extra-lines ()\r
- "Change all groups of blank lines to 1 blank line."\r
- (interactive)\r
- (save-excursion\r
- (replace-regexp "^\n+"\r
- "\n"\r
- nil\r
- (point-min)\r
- (point-max))))\r
-\r
-(setq require-final-newline t)\r
-(add-hook 'before-save-hook #'re/flush-extra-lines)\r
-(add-hook 'before-save-hook #'whitespace-cleanup)\r
-(add-hook 'after-save-hook #'executable-make-buffer-file-executable-if-script-p)\r
-#+END_SRC\r
-* Pass-alike\r
-** Password generation\r
-#+BEGIN_SRC emacs-lisp\r
-(defconst *alphanumerics*\r
- (mapcar #'char-to-string\r
- (append (number-sequence ?a ?z)\r
- (number-sequence ?A ?Z)\r
- (number-sequence ?0 ?9)))\r
- "List of strings of the upper/lower-case English alphabet, and the single-digit numbers.")\r
-\r
-(defconst *special-characters*\r
- '("!" "?" "$" "%" "&" "'" "(" ")" "*" "+" "," "-" "."\r
- "/" ":" ";" "<" "=" ">" "?" "@" "[" "]" "^" "_" "{"\r
- "|" "}" "~")\r
- "List of strings containing the special visible characters excluding \\ and \" .")\r
-\r
-(defun pw-get-char (&optional no-special-chars)\r
- "Select a single character at random to be used in a password.\r
-If `no-special-chars' is t, use only alpha-numeric characters."\r
- (let ((selected-charset (append *alphanumerics*\r
- (when (null no-special-chars)\r
- ,*special-characters*))))\r
- (elt selected-charset (random (length selected-charset)))))\r
-\r
-(defun pw-get-chars (password-length &optional no-special-chars)\r
- "Generate a list of characters as strings to be made into a password.\r
-If `no-special-chars' is t, use only alpha-numeric characters."\r
- (append (list (pw-get-char no-special-chars))\r
- (when (> password-length 1)\r
- (pw-get-chars (- password-length 1) no-special-chars))))\r
-\r
-(defun generate-password (password-length &optional no-special-chars)\r
- "Combine a list of randomly-selected characters into a single string of length given.\r
-calls `pw-get-chars'. If `no-special-chars' is `t', use only alpha-numeric characters."\r
- (apply #'concat (pw-get-chars password-length no-special-chars)))\r
-\r
-(defun insert-generated-password (&optional password-length)\r
- "Insert a generated password into the current buffer.\r
-Negative password-length (or negative arg) sets `no-special-chars' for `generate-password'."\r
- (interactive "p")\r
- (let ((len (if (or (null password-length) (= 1 (abs password-length)))\r
- 16\r
- (abs password-length)))\r
- (no-special-p (and (numberp password-length) (< password-length 0))))\r
- (insert (generate-password len no-special-p))))\r
-#+END_SRC\r
-** Get a password from a pass(1) entry\r
-Assume the password is on the first line.\r
-#+BEGIN_SRC emacs-lisp\r
-(defun re/get-line-from-file (file-path line-number)\r
- "Return the specified LINE-NUMBER from FILE-PATH as a string.\r
-Start from bottom if given a negative line number."\r
- (with-temp-buffer\r
- (insert-file-contents file-path)\r
- (if (< line-number 0)\r
- (progn\r
- (goto-char (point-max))\r
- (forward-line line-number))\r
- (progn\r
- (goto-char (point-min))\r
- (forward-line (1- line-number))))\r
- (buffer-substring-no-properties (line-beginning-position) (line-end-position))))\r
-\r
-(defun get-password (&optional filename)\r
- "Return a password given a file where it is stored.\r
-Assumes same structure as `pass(1)'."\r
- (re/get-line-from-file (keyfile-string filename) 1))\r
-\r
-(defun password-to-clipboard (&optional filename)\r
- "Copy the returned password to the clipboard."\r
- (interactive)\r
- (kill-new (get-password filename)))\r
-#+END_SRC\r
-** TOTP\r
-Generate TOTP tokens, generate the codes, put them on the clipboard. Assumes the format of pass(1), where the TOTP token is the last line in the file.\r
-#+BEGIN_SRC emacs-lisp\r
-(dolist (requirement '(bindat gnutls hexl auth-source))\r
- (require requirement))\r
-\r
-(defun totp--hex-decode-string (string)\r
- "Hex-decode STRING and return the result as a unibyte string."\r
- (apply #'unibyte-string\r
- (seq-map (lambda (s) (hexl-htoi (aref s 0) (aref s 1)))\r
- (seq-partition string 2))))\r
-\r
-(defun totp (string &optional time digits)\r
- "Return a TOTP token using the secret hex STRING and current time.\r
-TIME is used as counter value instead of current time, if non-nil.\r
-DIGITS is the number of pin digits and defaults to 6."\r
- (let* ((key-bytes (totp--hex-decode-string (upcase string)))\r
- (counter (truncate (/ (or time (time-to-seconds)) 30)))\r
- (digits (or digits 6))\r
- (format-string (format "%%0%dd" digits))\r
- ;; we have to manually split the 64 bit number (u64 not supported in Emacs 27.2)\r
- (counter-bytes (bindat-pack '((:high u32) (:low u32))\r
- `((:high . ,(ash counter -32)) (:low . ,(logand counter #xffffffff)))))\r
- (mac (gnutls-hash-mac 'SHA1 key-bytes counter-bytes))\r
- (offset (logand (bindat-get-field (bindat-unpack '((:offset u8)) mac 19) :offset) #xf)))\r
- (format format-string\r
- (mod\r
- (logand (bindat-get-field (bindat-unpack '((:totp-pin u32)) mac offset) :totp-pin)\r
- #x7fffffff)\r
- (expt 10 digits)))))\r
-\r
-(defconst base32-alphabet\r
- (let ((tbl (make-char-table nil)))\r
- (dolist (mapping '(("A" . 0) ("B" . 1) ("C" . 2) ("D" . 3)\r
- ("E" . 4) ("F" . 5) ("G" . 6)\r
- ("H" . 7) ("I" . 8) ("J" . 9) ("K" . 10)\r
- ("L" . 11) ("M" . 12) ("N" . 13)\r
- ("O" . 14) ("P" . 15) ("Q" . 16) ("R" . 17)\r
- ("S" . 18) ("T" . 19) ("U" . 20)\r
- ("V" . 21) ("W" . 22) ("X" . 23) ("Y" . 24)\r
- ("Z" . 25) ("2" . 26) ("3" . 27)\r
- ("4" . 28) ("5" . 29) ("6" . 30) ("7" . 31)))\r
- (aset tbl (string-to-char (car mapping)) (cdr mapping)))\r
- tbl)\r
- "Base-32 mapping table, as defined in RFC 4648.")\r
-\r
-(defun base32-hex-decode (string)\r
- "The cheats' version of base-32 decode.\r
-\r
-This is not a 100% faithful implementation of RFC 4648. The\r
-concept of encoding partial quanta is not implemented fully.\r
-\r
-No attempt is made to pad the output either as that is not\r
-required for HMAC-TOTP."\r
- (unless (mod (length string) 8)\r
- (error "Padding is incorrect"))\r
- (setq string (upcase string))\r
- (let ((trimmed-array (append (string-trim-right string "=+") nil)))\r
- (format "%X" (seq-reduce\r
- (lambda (acc char) (+ (ash acc 5) (aref base32-alphabet char)))\r
- trimmed-array 0))))\r
-\r
-(defun keyfile-string (filename)\r
- "Return a string which is the filepath to a `pass(1)' file."\r
- (let ((directory "~/.password-store/"))\r
- (if (null filename)\r
- (read-file-name "Select a password: " directory nil t nil)\r
- (concat directory\r
- (if (stringp filename)\r
- filename\r
- (symbol-name filename))\r
- ".gpg"))))\r
-\r
-(defun get-totp (&optional filename)\r
- "Generate a totp code given a file with the secret hex string.\r
-If no filename given, prompt for one.\r
-Assumes the file has the string by itself on the last line of the file,\r
-similar to what pass-otp does, but without the full uri.\r
-Also assumes the file is in `~/.password-store' and has the `.gpg' extension."\r
- (totp (base32-hex-decode (re/get-line-from-file (keyfile-string filename) -1))))\r
-\r
-(defun otp-to-clipboard (&optional filename)\r
- "Copy the returned totp code to the clipboard."\r
- (interactive)\r
- (kill-new (get-totp filename)))\r
-#+END_SRC\r
-* Control\r
-#+BEGIN_SRC emacs-lisp\r
-(defun re/kill-emacs ()\r
- "Save all open files and kill emacs."\r
- (interactive)\r
- (save-some-buffers t)\r
- (kill-emacs))\r
-\r
-(keymap-global-set "C-x M-c" #'re/kill-emacs)\r
-\r
-(defun re/restart-emacs ()\r
- "Save all open files and restart emacs."\r
- (interactive)\r
- (save-some-buffers t)\r
- (kill-emacs nil t))\r
-\r
-(keymap-global-set "C-x M-r" #'re/restart-emacs)\r
-\r
-(defun re/keyboard-quit ()\r
- "Smarter version of `keyboard-quit'.\r
-Close the minibuffer if not focused when hit.\r
-Stolen from emacsredux.com."\r
- (interactive)\r
- (if (active-minibuffer-window)\r
- (if (minibufferp)\r
- (minibuffer-keyboard-quit)\r
- (abort-recursive-edit))\r
- (keyboard-quit)))\r
-\r
-(keymap-global-set "<remap> <keyboard-quit>" #'re/keyboard-quit) ; Make C-g better\r
-(keymap-global-set "C-x S" (lambda () (interactive) (save-some-buffers t))) ; don't ask\r
-(keymap-global-set "C-x c" #'delete-frame) ; delete a frame without asking about saving files\r
-#+END_SRC\r
-* Version-control mode\r
-#+BEGIN_SRC emacs-lisp\r
-(defun re/vc-clone ()\r
- "Interactively clone with vc-mode.\r
-Prompt for url and local dir."\r
- (interactive)\r
- (let* ((url (read-string "Repository URL: "))\r
- (dir (read-string "Local Dir: " (file-name-base url))))\r
- (vc-git-clone url dir nil)))\r
-\r
-(keymap-global-set "C-x v C" #'re/vc-clone)\r
-\r
-(defun re/vc-show-branches (&optional arg)\r
- "Display all Git branches in a separate buffer.\r
-Remotes as well when arg."\r
- (interactive "P")\r
- (let ((default-directory (if (boundp 'vc-dir-directory)\r
- vc-dir-directory\r
- default-directory)))\r
- (vc-git-command "*git-branches*"\r
- nil\r
- nil\r
- "branch"\r
- "--verbose"\r
- (when arg "--remotes"))\r
- (pop-to-buffer "*git-branches*")\r
- (goto-char (point-min))\r
- (special-mode)))\r
-\r
-(keymap-global-set "C-x v b b" #'re/vc-show-branches)\r
-\r
-(defun re/vc-fetch (&optional arg)\r
- "Interactively fetch with vc-mode.\r
-Allows separate fetch in addition to pull. Only care about git.\r
-With arg, ask for remote, otherwise fetch all."\r
- (interactive "P")\r
- (let* ((default-directory (if (boundp 'vc-dir-directory)\r
- vc-dir-directory\r
- default-directory)))\r
- (vc-git-command "*vc-fetch*"\r
- nil\r
- nil\r
- "fetch"\r
- (if arg\r
- (read-string "Fetch From: " "origin")\r
- "--all")\r
- "--verbose")\r
- (pop-to-buffer "*vc-fetch*")\r
- (goto-char (point-min))\r
- (special-mode)))\r
-\r
-(keymap-global-set "C-x v f" #'re/vc-fetch)\r
-\r
-(add-hook 'vc-dir-mode-hook\r
- (lambda ()\r
- (keymap-set vc-dir-mode-map "b b" #'re/vc-show-branches)\r
- (keymap-set vc-dir-mode-map "f" #'re/vc-fetch)\r
- (keymap-set vc-dir-mode-map "k" #'vc-revert)))\r
-\r
-(setq vc-make-backup-files nil\r
- vc-handled-backends '(jj Git)\r
- vc-git-log-switches '("--oneline" "--graph" "--decorate" "--all")\r
- vc-git-log-edit-summary-target-len 50\r
- vc-find-revision-no-save t)\r
-#+END_SRC\r
-* Backups\r
-#+BEGIN_SRC emacs-lisp\r
-(setq backup-directory-alist `((".*" . "~/emacs-backups/"))\r
- backup-by-copying t\r
- delete-old-versions t\r
- kept-new-versions 5\r
- kept-old-versions 5)\r
-#+END_SRC\r
-* Any Lisp Repl\r
-#+BEGIN_SRC emacs-lisp\r
-(defmacro re/send-on-close-paren (executor)\r
- "Generalizes sending an execute on close paren.\r
- Interactively call `executor'. For `executor', use whatever\r
- function is called by `<RET>' in the applicable REPL. For a\r
- non-repl, use `newline' or similar."\r
- `(lambda (&optional arg)\r
- (interactive "p")\r
- (insert-char 41 arg)\r
- (call-interactively ,executor)))\r
-\r
-(defun re/sexp-drop-paren-p ()\r
- "Returns t if sexp needs fewer parens to balance."\r
- (< (car (syntax-ppss)) 0))\r
-\r
-(defun re/sexp-need-paren-p ()\r
- "Returns t if sexp needs more parens to balance."\r
- (> (car (syntax-ppss)) 0))\r
-\r
-(defun re/sexp-unbalanced-count ()\r
- "Returns distance from balanced sexp."\r
- (abs (car (syntax-ppss))))\r
-\r
-(defun re/balance-sexp ()\r
- "Balance the sexp before point.\r
-Either delete, move forward, or add as many ')' as needed."\r
- (let ((distance (re/sexp-unbalanced-count)))\r
- (cond ((re/sexp-need-paren-p)\r
- (if (looking-at ")")\r
- (progn\r
- (forward-char)\r
- (re/balance-sexp))\r
- (insert-char 41 distance)))\r
- ((re/sexp-drop-paren-p)\r
- (backward-delete-char distance)))))\r
-\r
-(defmacro re/balance-and-eval-sexp (executor)\r
- "Balance the sexp before point, then call `executor'.\r
-Call `re/balance-sexp', then interactively call `executor'. Bind\r
-this to ']' for the interlisp experience. For `executor', use\r
-whatever function is called by `<RET>' in the applicable REPL.\r
-For a non-repl, use `newline' or similar."\r
- `(lambda () (interactive) (re/balance-sexp) (call-interactively ,executor)))\r
-\r
-(defvar *re/lisp-mode-hooks*\r
- '(emacs-lisp-mode-hook\r
- lisp-mode-hook\r
- sly-mode-hook\r
- scheme-mode-hook\r
- geiser-mode-hook\r
- lisp-interaction-mode-hook))\r
-\r
-(dolist (hook *re/lisp-mode-hooks*)\r
- (add-hook hook\r
- (lambda ()\r
- (when (bound-and-true-p acme-mouse-mode) (acme-mouse-mode -1))\r
- (sedit-mouse-mode 1)\r
- (keymap-set lisp-mode-shared-map "C-M-z" #'eval-region)\r
- (keymap-set lisp-mode-shared-map\r
- "C-M-S-q"\r
- #'sedit-auto-prettify-sexp)\r
- (keymap-set lisp-mode-shared-map\r
- "]"\r
- (re/balance-and-eval-sexp #'sedit-auto-prettify-sexp))\r
- (keymap-set lisp-interaction-mode-map\r
- "]"\r
- (re/balance-and-eval-sexp #'eval-print-last-sexp)))))\r
-#+END_SRC\r
-* IELM\r
-Rarely used, but nice to have when I want it.\r
-#+BEGIN_SRC emacs-lisp\r
-(add-hook 'ielm-mode-hook\r
- (lambda ()\r
- (keymap-set inferior-emacs-lisp-mode-map\r
- "C-l"\r
- #'comint-clear-buffer)\r
- (let ((eval-function #'ielm-send-input))\r
- (keymap-set inferior-emacs-lisp-mode-map\r
- ")"\r
- (re/send-on-close-paren eval-function))\r
- (keymap-set inferior-emacs-lisp-mode-map\r
- "]"\r
- (re/balance-and-eval-sexp eval-function)))))\r
-#+END_SRC\r
-* Eshell\r
-#+BEGIN_SRC emacs-lisp\r
-(keymap-global-set "C-c e" #'eshell)\r
-\r
-(defun eshell-quit-or-delete-char (arg)\r
- "Delete char if at one, quit eshell if on empty prompt.\r
-Stolen from https://depp.brause.cc/dotemacs"\r
- (interactive "p")\r
- (if (and (eolp) (looking-back eshell-prompt-regexp 0 t))\r
- (eshell-life-is-too-much)\r
- (delete-char arg)))\r
-\r
-(add-hook 'eshell-mode-hook\r
- (lambda ()\r
- (keymap-set eshell-mode-map "C-d" #'eshell-quit-or-delete-char)\r
- (let ((eval-function #'eshell-send-input))\r
- (keymap-set eshell-mode-map\r
- ")"\r
- (re/send-on-close-paren eval-function))\r
- (keymap-set eshell-mode-map\r
- "]"\r
- (re/balance-and-eval-sexp eval-function)))))\r
-#+END_SRC\r
-* C-mode\r
-#+BEGIN_SRC emacs-lisp\r
-(dolist (mode-maps '(("\\.keymap\\'" . c-mode) ("\\.dtsi\\'" . c-mode)))\r
- (add-to-list 'auto-mode-alist mode-maps t))\r
-\r
-(defun re/c-lineup-arglist-tabs-only (ignored)\r
- "Line up argument lists by tabs, not spaces.\r
- Stolen from https://kernel.org/doc/html/v4.10/process/coding-style.html"\r
- (let* ((anchor (c-langelem-pos c-syntactic-element))\r
- (column (c-langelem-2nd-pos c-syntactic-element))\r
- (offset (- (1+ column) anchor))\r
- (steps (floor offset c-basic-offset)))\r
- (* (max steps 1) c-basic-offset)))\r
-\r
-(add-hook 'c-mode-common-hook\r
- (lambda ()\r
- (c-add-style "linux-tabs-only"\r
- '("linux" (c-offsets-alist\r
- (arglist-cont-nonempty\r
- c-lineup-gcc-asm-reg\r
- re/c-lineup-arglist-tabs-only))))))\r
-\r
-(add-hook 'c-mode-hook\r
- (lambda ()\r
- (setq c-backspace-function 'backward-delete-char)\r
- (c-set-style "linux-tabs-only")))\r
-#+END_SRC\r
-* Auto-Revert\r
-#+BEGIN_SRC emacs-lisp\r
-(global-auto-revert-mode)\r
-(setq global-auto-revert-non-file-buffers nil)\r
-#+END_SRC\r
-* Crash HandliĆ\r
-#+BEGIN_SRC emacs-lisp\r
-(setq attempt-stack-overflow-recovery nil\r
- attempt-orderly-shutdown-on-fatal-signal nil)\r
-#+END_SRC\r
-* Abbrev-mode and skeletons\r
-#+BEGIN_SRC emacs-lisp\r
-(define-skeleton workorder-link\r
- "part of a link to a workorder, just enter the number."\r
- ""\r
- "https://eservice.r717.net/index.php/PLC_workorders/store/"\r
- _ -)\r
-\r
-(define-skeleton skel-org-block\r
- "Add a source block to an org file."\r
- ""\r
- "#+BEGIN_SRC"\r
- \n\r
- _ -\r
- \n\r
- "#+END_SRC")\r
-\r
-(define-skeleton skel-org-block-elisp\r
- "Add an elisp source block to an org file."\r
- ""\r
- "#+BEGIN_SRC emacs-lisp"\r
- \n\r
- _ -\r
- \n\r
- "#+END_SRC")\r
-\r
-(setq save-abbrevs 'silently)\r
-\r
-(add-hook 'org-mode-hook #'abbrev-mode)\r
-#+END_SRC\r
-* Customization Framework\r
-Disable it by making it save to a temp file (stolen from prot)\r
-#+BEGIN_SRC emacs-lisp\r
-(setq custom-file (make-temp-file "emacs-custom-"))\r
-#+END_SRC\r
-;;; init.el --- Emacs Configuration -*- lexical-binding: t; -*-
-
-;; This file is not part of GNU emacs
-
-;;; Code:
-
-(org-babel-load-file "~/.emacs.d/config.org")
-
-;;; init.el ends here
+;;;; init.el --- Emacs Configuration -*- lexical-binding: t; -*-\r
+\r
+;;; OS specific stuff\r
+\r
+(setq w32-recognize-altgr 'nil)\r
+\r
+(unless (equal system-type 'windows-nt)\r
+ (setq trash-directory "~/Trash/")\r
+ (defun doas ()\r
+ "Use TRAMP to reopen the current buffer as root using doas."\r
+ (interactive)\r
+ (when buffer-file-name\r
+ (find-alternate-file (concat "/doas:root@localhost:" buffer-file-name))))\r
+ (defun sudo ()\r
+ "Use TRAMP to reopen the current buffer as root using sudo."\r
+ (interactive)\r
+ (when buffer-file-name\r
+ (find-alternate-file (concat "/sudo:root@localhost:" buffer-file-name)))))\r
+\f\r
+;;; PACKAGES\r
+;; Add melpa, initialize\r
+\r
+(require 'package)\r
+\r
+(add-to-list 'package-archives '("melpa" . "https://melpa.org/packages/") t)\r
+(package-initialize)\r
+\r
+;; Install all packages that are not included\r
+\r
+(defvar *re/packages*\r
+ '(corfu\r
+ expand-region\r
+ page-break-lines\r
+ pdf-tools\r
+ auctex\r
+ csv-mode\r
+ markdown-mode\r
+ edit-indirect\r
+ sly\r
+ geiser\r
+ geiser-chicken\r
+ forth-mode\r
+ trashed\r
+ org-transclusion\r
+ vc-jj\r
+ minions)\r
+ "The list of packages I want to always install")\r
+\r
+(dolist (pkg *re/packages*)\r
+ (unless (package-installed-p pkg)\r
+ (package-refresh-contents)\r
+ (package-install pkg)))\r
+\f\r
+;; Corfu\r
+\r
+(require 'corfu)\r
+\r
+(setq corfu-auto t\r
+ corfu-auto-delay 0\r
+ corfu-auto-prefix 1)\r
+\r
+(add-hook 'corfu-mode-hook #'corfu-popupinfo-mode)\r
+\r
+(setq corfu-popupinfo-delay '(1.0 . 0.01) ; start after one second, fast refresh once on\r
+ corfu-popupinfo-hide nil)\r
+\r
+(global-corfu-mode)\r
+\f\r
+;; Expand Region\r
+\r
+(require 'expand-region)\r
+\r
+(keymap-global-set "C-=" #'er/expand-region)\r
+\f\r
+;; Page Break Lines\r
+\r
+(require 'page-break-lines)\r
+(global-page-break-lines-mode)\r
+\f\r
+;; PDFtools\r
+;; Be sure to run `pdf-tools-install' before first run\r
+\r
+(add-to-list 'auto-mode-alist '("\\.pdf\\'" . pdf-tools-install))\r
+\r
+(setq-default pdf-view-display-size 'fit-page)\r
+\r
+;; Auctex\r
+\r
+(setq-default TeX-master nil)\r
+\r
+(setq TeX-auto-save t\r
+ TeX-parse-self t\r
+ TeX-PDF-mode t\r
+ TeX-view-program-selection '((output-pdf "PDF Tools"))\r
+ TeX-source-correlate-start-server t)\r
+\r
+(add-hook 'TeX-after-compilation-finished-functions\r
+ #'TeX-revert-document-buffer)\r
+\f\r
+;; CSV Mode\r
+\r
+(add-hook 'csv-mode-hook 'csv-align-mode)\r
+\f\r
+;; Markdown Mode\r
+;; Make markdown act more like org when I'm forced to use it\r
+\r
+(setq markdown-special-ctrl-a/e t)\r
+\r
+(with-eval-after-load 'markdown-mode\r
+ (keymap-set markdown-mode-map "M-<right>" #'markdown-demote)\r
+ (keymap-set markdown-mode-map "M-<left>" #'markdown-promote)\r
+ (keymap-set markdown-mode-map "M-<up>" #'markdown-move-up)\r
+ (keymap-set markdown-mode-map "M-<down>" #'markdown-move-down)\r
+ (keymap-set markdown-mode-map "C-M-u" #'markdown-up-heading)\r
+ (keymap-set markdown-mode-map "C-M-b" #'markdown-outline-previous-same-level)\r
+ (keymap-set markdown-mode-map "C-M-f" #'markdown-outline-next-same-level)\r
+ (keymap-set markdown-mode-map "C-M-p" #'markdown-outline-previous)\r
+ (keymap-set markdown-mode-map "C-M-n" #'markdown-outline-next))\r
+\f\r
+;; Sly\r
+\r
+(defun re/sly-repl-new-frame (&optional arg)\r
+ "Start a SLY repl in a new frame."\r
+ (interactive "P")\r
+ (re/open-with-new-frame "SLY" #'sly))\r
+\r
+(defun re/sly-quit-current-lisp ()\r
+ "Quit the current lisp repl connection without prompting."\r
+ (interactive)\r
+ (sly-quit-lisp nil nil))\r
+\r
+(defun re/sly-connect-stump (&optional arg)\r
+ "Connect to my typical stumpwm instance."\r
+ (interactive "P")\r
+ (sly-connect "localhost" 4004))\r
+\r
+(setq inferior-lisp-program "sbcl"\r
+ sly-net-coding-system 'utf-8-unix\r
+ sly-command-switch-to-existing-lisp 'always)\r
+\r
+(let ((core (expand-file-name "~/sbcl.core-for-sly")))\r
+ (when (file-exists-p core)\r
+ (setq sly-lisp-implementations (list `(sbcl ("sbcl" "--core" ,core))))))\r
+\r
+(with-eval-after-load 'sly-mrepl\r
+ (keymap-set sly-mrepl-mode-map ")" (re/send-on-close-paren #'sly-mrepl-return))\r
+ (keymap-set sly-mrepl-mode-map "]" (re/balance-and-eval-sexp #'sly-mrepl-return))\r
+ (keymap-set sly-mrepl-mode-map "C-c q" #'re/sly-quit-current-lisp))\r
+\f\r
+;; Eldoc settings\r
+;; Sly really doesn't play well with elodc in the minibuffer,\r
+;; so here are some settings to make it better.\r
+\r
+(add-to-list 'display-buffer-alist\r
+ '("^\\*eldoc" display-buffer-in-direction\r
+ (direction . 'down)\r
+ (window-height . 4)))\r
+(keymap-global-set "C-c d" #'eldoc) ; force eldoc to show in minibuffer\r
+(keymap-global-set "C-c D" #'eldoc-doc-buffer) ; open eldoc buffer when it's getting spammed\r
+\f\r
+;; Geiser\r
+\r
+(if (equal system-type 'windows-nt)\r
+ (setq geiser-chicken-binary '("C:/tools/chicken/bin/csi.exe" "-:c"))\r
+ (setq geiser-chicken-binary "csi -:c"))\r
+\r
+(setq scheme-program-name geiser-chicken-binary\r
+ geiser-active-implementations '(chicken))\r
+\r
+(add-hook 'scheme-mode-hook 'geiser-mode)\r
+\r
+(with-eval-after-load 'geiser-repl\r
+ (keymap-set geiser-repl-mode-map ")" (re/send-on-close-paren #'geiser-repl-maybe-send))\r
+ (keymap-set geiser-repl-mode-map "]" (re/balance-and-eval-sexp #'geiser-repl-maybe-send)))\r
+\r
+;; Set bindings, things that have to happen after loading\r
+\r
+(with-eval-after-load 'org\r
+ (require 'org-mouse)\r
+ (add-to-list 'org-export-backends 'md)\r
+ (keymap-set org-mode-map "C-M-h" #'org-mark-subtree)\r
+ (keymap-set org-mode-map "C-M-u" #'org-up-element)\r
+ (keymap-set org-mode-map "C-M-d" #'org-down-element)\r
+ (keymap-set org-mode-map "C-M-b" #'org-backward-heading-same-level)\r
+ (keymap-set org-mode-map "C-M-f" #'org-forward-heading-same-level)\r
+ (keymap-set org-mode-map "C-M-p" #'org-previous-visible-heading)\r
+ (keymap-set org-mode-map "C-M-n" #'org-next-visible-heading)\r
+ (keymap-set org-mode-map "C-c t" #'org-transclusion-mode))\r
+\r
+(keymap-global-set "C-c l" #'org-store-link)\r
+(keymap-global-set "C-c a" #'org-agenda)\r
+(keymap-global-set "C-c c" #'org-capture)\r
+\f\r
+;; Org-Mode\r
+;; General options\r
+\r
+(setq org-M-RET-may-split-line nil\r
+ org-cycle-inline-images-display t\r
+ org-cycle-separator-lines -1\r
+ org-ellipsis "->"\r
+ org-enforce-todo-checkbox-dependencies t\r
+ org-enforce-todo-dependencies t\r
+ org-goto-interface 'outline-path-completion\r
+ org-outline-path-complete-in-steps nil\r
+ org-src-fontify-natively nil\r
+ org-return-follows-link t\r
+ org-reverse-note-order t\r
+ org-special-ctrl-a/e t\r
+ org-special-ctrl-k t\r
+ org-src-preserve-indentation t\r
+ org-startup-folded 'fold\r
+ org-tags-column 0\r
+ org-yank-adjusted-subtrees t\r
+ org-refile-use-outline-path 'file\r
+ org-refile-allow-creating-parent-nodes 'confirm)\r
+\r
+;; Agenda settings\r
+\r
+ (setq org-agenda-files (append '("~/org/")\r
+ (file-expand-wildcards "~/projects/*/"))\r
+ org-refile-targets '((nil :maxlevel . 9)\r
+ (org-agenda-files :maxlevel . 9))\r
+ org-todo-keywords '((sequence "TODO(t)" "WAITING(w@)" "|" "DONE(d)" "BELAYED(b@)" "GIVEN(g@)"))\r
+ org-todo-keyword-faces '(("WAITING" :foreground "#e85400" :weight bold)) ; alert orange\r
+ org-agenda-prefix-format '((agenda . " %?-12t%? s")\r
+ (todo . " ")\r
+ (tags . " ")\r
+ (search . " "))\r
+ org-agenda-start-on-weekday nil\r
+ org-read-date-prefer-future 'time\r
+ org-log-done 'time\r
+ org-log-redeadline 'time\r
+ org-log-reschedule 'time\r
+ org-log-state-notes-insert-after-drawers t\r
+ org-agenda-window-setup 'current-window)\r
+\r
+;; Stuck Projects\r
+\r
+(setq org-stuck-projects '("CATEGORY=\"undertakings\"+LEVEL=1/-DONE-GIVEN-BELAYED" ("TODO" "WAITING") nil ""))\r
+\r
+;; Custom Agendas\r
+\r
+ (defvar *re/org-agenda-inbox*\r
+ '(tags "CATEGORY=\"inbox\"" ((org-agenda-overriding-header ""))))\r
+\r
+ (defvar *re/org-agenda-todo*\r
+ '(tags-todo "-recurring/TODO" ((org-agenda-overriding-header ""))))\r
+\r
+ (defvar *re/org-agenda-undertakings*\r
+ '(tags "CATEGORY=\"undertakings\"+LEVEL=1" ((org-agenda-overriding-header ""))))\r
+\r
+ (defvar *re/org-agenda-waiting*\r
+ '(todo "WAITING" ((org-agenda-overriding-header ""))))\r
+\r
+(defvar *re/org-agenda-done*\r
+ '(tags "-recurring+LEVEL=1/DONE|BELAYED|GIVEN"))\r
+\r
+ (setq org-agenda-custom-commands `(("i" "Inbox" ,@*re/org-agenda-inbox*)\r
+ ("n" "Next Actions" ,@*re/org-agenda-todo*)\r
+ ("u" "Undertakings" ,@*re/org-agenda-undertakings*)\r
+ ("w" "Waiting" ,@*re/org-agenda-waiting*)\r
+ ("d" "Done" ,@*re/org-agenda-done*)))\r
+\r
+;; Capture Templates\r
+\r
+ (setq org-capture-templates '(("i" "Inbox" entry\r
+ (file "inbox.org")\r
+ "* %^{Description}\n%U\n%?"\r
+ :prepend t)\r
+ ("n" "Next Action" entry\r
+ (file "next.org")\r
+ "* TODO %^{Description}\nTaken: %T\n%?"\r
+ :prepend t)\r
+ ("u" "Undertaking" entry\r
+ (file "undertakings.org")\r
+ "* %^{Description} [/]\nTaken: %T\n%?"\r
+ :prepend t)\r
+ ("m" "Meeting" entry\r
+ (file "meetings.org")\r
+ "* %^{Description}\n%^{Scheduled:}T\n%?"\r
+ :prepend t)\r
+ ("p" "Phone Call" entry\r
+ (file "inbox.org")\r
+ "* %^{Description} :call:\n%T\n%?"\r
+ :prepend t)\r
+ ("t" "Note" entry\r
+ (file "notes.org")\r
+ "* %^{Description}\n%U\n%?"\r
+ :prepend t)))\r
+\r
+;; IDs\r
+\r
+(setq org-id-link-to-org-use-id 'create-if-interactive-and-no-custom-id\r
+ org-id-method 'ts\r
+ org-clone-delete-id t\r
+ org-attach-id-to-path-function-list '(org-attach-id-ts-folder-format\r
+ org-attach-id-uuid-folder-format))\r
+\r
+(defun re/org-save-all ()\r
+ "Save all `org-agenda-files' without user confirmation."\r
+ (interactive)\r
+ (message "Saving all org-agenda-files buffers...")\r
+ (save-some-buffers t\r
+ (lambda ()\r
+ (when (member (buffer-file-name) org-agenda-files) t)))\r
+ (message "Saving all org-agenda-files buffers... done"))\r
+\r
+(advice-add 'org-refile :after (lambda (&rest _) (re/org-save-all)))\r
+\r
+;; Archiving\r
+\r
+(setq org-archive-location (concat "~/org/old.org_keep::datetree/")\r
+ org-agenda-text-search-extra-files '(agenda-archives))\r
+\r
+(add-to-list 'auto-mode-alist '("\\.org_keep\\'" . org-mode))\r
+\r
+;; Converting markdown to org\r
+\r
+(defun re/markdown-to-org-region (start end)\r
+ "Convert Markdown formatted text in region (START, END) to Org.\r
+This command requires that pandoc (man page `pandoc(1)') be\r
+installed."\r
+ (interactive "r")\r
+ (shell-command-on-region\r
+ start end\r
+ "pandoc -f markdown -t org --wrap=preserve" t t))\r
+\r
+;; Converting org to markdown\r
+\r
+(defun re/org-to-markdown-region (start end)\r
+ "Convert Org formatted text in region (START, END) to Markdown.\r
+This command requires that pandoc (man page `pandoc(1)') be\r
+installed."\r
+ (interactive "r")\r
+ (shell-command-on-region\r
+ start end\r
+ "pandoc -f org -t markdown --wrap=preserve" t t))\r
+\f\r
+;; Trashed\r
+;; Shows the trash file as a dired buffer\r
+\r
+(setq trashed-action-confirmer 'y-or-n-p\r
+ trashed-use-header-line t\r
+ trashed-sort-key '("Date deleted" . t)\r
+ trashed-date-format "%Y-%m-%d %H:%M:%S")\r
+\f\r
+;; Use mu4e on my home pcs\r
+\r
+(when (eq system-type 'gnu/linux)\r
+ (with-eval-after-load 'mu4e\r
+ (setq mail-user-agent 'message-user-agent\r
+ message-send-mail-function 'smtpmail-send-it\r
+ message-citation-line-format "Quoth %f :\n"\r
+ smtpmail-default-smtp-server "mail.earman.xyz"\r
+ smtpmail-smtp-server "mail.earman.xyz"\r
+ smtpmail-smtp-service 587\r
+ smtpmail-stream-type 'starttls\r
+ smtpmail-local-domain "earman.xyz"\r
+ smtpmail-queue-mail nil\r
+ smtpmail-queue-dir "~/mail/Outbox/cur"\r
+ user-mail-address "iv@earman.xyz"\r
+ user-full-name "Rush N. Earman IV"\r
+ message-signature "Rush Earman IV, PE"\r
+ message-kill-buffer-on-exit t\r
+ mu4e-get-mail-command "mbsync -a"\r
+ mu4e-update-interval (* 10 60)\r
+ mu4e-change-filenames-when-moving t\r
+ mu4e-use-fancy-chars t\r
+ mu4e-attachment-dir "~/downloads"\r
+ mu4e-compose-reply-to-address "iv@earman.xyz"\r
+ mu4e-maildir "~/mail"\r
+ mu4e-drafts-folder "/Drafts"\r
+ mu4e-sent-folder "/Sent"\r
+ mu4e-trash-folder "/Trash"\r
+ mu4e-maildir-shortcuts '((:maildir "/Inbox" :key ?i)\r
+ (:maildir "/Sent" :key ?s)\r
+ (:maildir "/Trash" :key ?t)\r
+ (:maildir "/Drafts" :key ?d)))))\r
+\f\r
+;;; Load libraries Not from the package manager\r
+\r
+(dolist (path '("sedit-mouse/" "themes/"))\r
+ (add-to-list 'load-path (concat user-emacs-directory path)))\r
+\r
+(dolist (file '(eng-paper-theme sedit-mouse))\r
+ (require file))\r
+\f\r
+;;; UNIX/vi-like bindings\r
+;; =C-h=, =C-w=, =C-u=, et. al to work as I expect (like acme).\r
+;; Some functions get re-bound when these take over a default.\r
+\r
+(defun re/open-line-below (n)\r
+ "Creates a new empty line below the current line and moves to it."\r
+ (interactive "*p")\r
+ (end-of-line)\r
+ (open-line n)\r
+ (next-line 1)\r
+ (indent-for-tab-command))\r
+\r
+(keymap-global-set "C-o" #'re/open-line-below)\r
+\r
+(defun re/open-line-above (n)\r
+ "Creates a new empty line above the current line."\r
+ (interactive "*p")\r
+ (beginning-of-line)\r
+ (open-line n)\r
+ (indent-for-tab-command))\r
+\r
+(keymap-global-set "M-o" #'re/open-line-above)\r
+\r
+(defun re/kill-bword-or-region (&optional arg)\r
+ "Kill region if active, otherwise kill back one word."\r
+ (interactive "p")\r
+ (if (use-region-p)\r
+ (kill-region (region-beginning) (region-end))\r
+ (backward-kill-word arg)))\r
+\r
+(keymap-global-set "C-w" #'re/kill-bword-or-region)\r
+(keymap-global-set "C-u" (lambda () (interactive) (kill-line 0))) ; kill-line backwards\r
+(keymap-global-set "C-S-U" #'universal-argument)\r
+(keymap-set key-translation-map "C-h" "<DEL>") ; Rather F1 for help, also C-S-H works\r
+(keymap-global-set "M-j" (lambda () (interactive) (join-line 0))) ; join-line other way\r
+(keymap-global-set "<home>" #'beginning-of-buffer)\r
+(keymap-global-set "<end>" #'end-of-buffer)\r
+\f\r
+;;; Commenting and defun-level bindings\r
+\r
+(defun re/comment-dwim (&optional arg)\r
+ "Comment region if active, otherwise comment line.\r
+ Stolen from https://depp.brause.cc/dotemacs"\r
+ (interactive "P")\r
+ (if (use-region-p)\r
+ (comment-or-uncomment-region (region-beginning) (region-end))\r
+ (comment-line arg)))\r
+\r
+(keymap-global-set "M-;" #'re/comment-dwim)\r
+\r
+(defun re/comment-defun (&optional arg)\r
+ "Mark a defun and comment it out.\r
+ Return to initial position after. Arg works on `mark-defun'."\r
+ (interactive "P")\r
+ (save-excursion (mark-defun arg)\r
+ (comment-region (region-beginning) (region-end))))\r
+\r
+(keymap-global-set "C-M-;" #'re/comment-defun)\r
+\r
+(defun re/kill-defun (&optional arg)\r
+ "Mark a defun and kill it.\r
+ Arg works on `mark-defun'."\r
+ (interactive "P")\r
+ (mark-defun arg)\r
+ (kill-region (region-beginning) (region-end)))\r
+\r
+(keymap-global-set "C-M-S-K" #'re/kill-defun)\r
+\f\r
+;;; Misc QOL bindings\r
+\r
+(keymap-global-set "<insert>" nil) ; I keep toggliĆ overwrite-mode haply\r
+(keymap-global-set "C-\\" #'undo-redo) ; issues with C-? sometimes\r
+(keymap-global-set "C-S-K" #'kill-whole-line)\r
+(keymap-global-set "C-S-d" #'delete-pair)\r
+(keymap-global-set "C-z" #'zap-up-to-char)\r
+(keymap-global-set "C-c s" #'sort-lines)\r
+(keymap-global-set "<remap> <capitalize-word>" #'capitalize-dwim) ; M-c\r
+(keymap-global-set "<remap> <downcase-word>" #'downcase-dwim) ; M-l\r
+(keymap-global-set "<remap> <upcase-word>" #'upcase-dwim) ; M-u\r
+\f\r
+;;; Tabs\r
+;; I want *TABS*, not spaces, dammit. Globally overwrite the tab key to\r
+;; insert the tab character. This makes it so that many things (completion)\r
+;; will have to be triggered with =C-i= instead, but I don't think it's\r
+;; necessarily bad to have things like that on a control combo instead of\r
+;; overloading the tab key at the expense of being able to actually insert\r
+;; tabs.\r
+\r
+(setq-default indent-tabs-mode t\r
+ tab-width 8\r
+ backward-delete-char-untabify-method nil)\r
+\r
+(advice-add 'indent-to :around\r
+ (lambda (orig-fun column &rest args)\r
+ (when indent-tabs-mode\r
+ (setq column (* tab-width (round column tab-width))))\r
+ (apply orig-fun column args)))\r
+\r
+(dolist (indent-level '(c-basic-offset cperl-indent-level LaTeX-indent-level))\r
+ (defvaralias indent-level 'tab-width))\r
+\r
+(defun Tab (width)\r
+ "Give me a function analagous to the Acme `Tab' command"\r
+ (interactive)\r
+ (setq-local tab-width width))\r
+\r
+(keymap-global-set "<tab>" (lambda () (interactive) (insert "\t")))\r
+\f\r
+;;; Buffer dealiĆ\r
+\r
+(setq-default indicate-empty-lines t\r
+ truncate-lines t\r
+ fill-column 78)\r
+\r
+(dolist (hook '(text-mode-hook LaTeX-mode-hook))\r
+ (add-hook hook #'visual-line-mode))\r
+\r
+(blink-cursor-mode -1)\r
+(delete-selection-mode t)\r
+\r
+(setq delete-by-moving-to-trash t\r
+ delete-pair-blink-delay 0\r
+ use-short-answers t\r
+ use-file-dialog nil\r
+ echo-keystrokes 0.1\r
+ eldoc-idle-delay 0\r
+ read-buffer-completion-ignore-case t\r
+ history-delete-duplicates t\r
+ disabled-command-function nil\r
+ save-interprogram-paste-before-kill t\r
+ confirm-kill-processes nil)\r
+\r
+(keymap-global-set "C-x M-f" #'find-file-at-point)\r
+(keymap-global-set "C-x C-b" #'buffer-menu)\r
+(keymap-global-set "C-x M-b" #'buffer-menu-other-window)\r
+(keymap-global-set "C-x k" #'kill-current-buffer)\r
+(keymap-global-set "C-x K" #'kill-buffer)\r
+\f\r
+;;; Recentering and page-by-page navigation\r
+\r
+(setq recenter-positions '(2 middle -3))\r
+\r
+(defun re/recenter-advice (&rest _)\r
+ "Recenter to page start."\r
+ (when (called-interactively-p 'any) (recenter 2)))\r
+\r
+(dolist (binding '(backward-page forward-page))\r
+ (advice-add binding :after #'re/recenter-advice))\r
+\r
+(keymap-global-set "C-x C-n" #'forward-page)\r
+(keymap-global-set "C-x C-p" #'backward-page)\r
+(keymap-global-set "<next>" #'forward-page)\r
+(keymap-global-set "<prior>" #'backward-page)\r
+\f\r
+;;; Macro to open something in its own named frame\r
+;; Start a repl in a new frame, open dired in a new frame, etc. etc.\r
+\r
+(defmacro re/open-with-new-frame (frname function)\r
+ "Run the function in its own named frame."\r
+ `(progn\r
+ (select-frame (make-frame '((name . ,(eval frname)))))\r
+ (call-interactively ,function)))\r
+\f\r
+;;; Scratch buffer easy access\r
+\r
+(defun re/scratch-only ()\r
+ "Bring up the scratch buffer as the only visible buffer."\r
+ (interactive)\r
+ (scratch-buffer)\r
+ (delete-other-windows))\r
+\r
+(keymap-global-set "<f5>" #'scratch-buffer)\r
+(keymap-global-set "S-<f5>" #'re/scratch-only)\r
+\f\r
+;;; Switch or split window\r
+;; Game changer. Auto choose how to split, or just switch to the next, all on\r
+;; the same binding. Ripped from an early version of the lem editor.\r
+\r
+(defun re/other-window-or-split-window (&optional window)\r
+ "Go to other window or split sensibly."\r
+ (interactive)\r
+ (if (= (count-windows) 1)\r
+ (funcall split-window-preferred-function window)\r
+ (other-window 1)))\r
+\r
+(keymap-global-set "C-M-o" #'re/other-window-or-split-window)\r
+\f\r
+;;; Smart End/Beginning of line\r
+;; Similar to what org does, and indeed calls the org function if in org-mode\r
+\r
+(defun re/smart-beginning-of-line ()\r
+ "Move point to first non-whitespace character or `beginning-of-visual-line'.\r
+If point was already at that position, call `back-to-indentation'.\r
+Called a third time, move point back to `beginning-of-line'.\r
+If in org-mode, call `org-beginning-of-line' first."\r
+ (interactive "^")\r
+ (let ((oldpos (point)))\r
+ (if (equal major-mode 'org-mode)\r
+ (org-beginning-of-line)\r
+ (progn\r
+ (when visual-line-mode (beginning-of-visual-line 1))\r
+ (skip-syntax-forward " " (line-end-position))\r
+ (backward-prefix-chars)))\r
+ (and (= oldpos (point))\r
+ (let ((newpos (point)))\r
+ (back-to-indentation)\r
+ (and (= newpos (point)) (beginning-of-line))))))\r
+\r
+(keymap-global-set "<remap> <move-beginning-of-line>" #'re/smart-beginning-of-line)\r
+(keymap-set visual-line-mode-map "C-a" #'re/smart-beginning-of-line)\r
+\r
+(defun re/smart-end-of-line ()\r
+ "Move point to the end of the visual line.\r
+ If point was already at that position, call `move-end-of-line'.\r
+If in org-mode, call `org-end-of-line' first."\r
+ (interactive "^")\r
+ (let ((oldpos (point)))\r
+ (if (equal major-mode 'org-mode)\r
+ (org-end-of-line)\r
+ (when visual-line-mode\r
+ (end-of-visual-line 1)))\r
+ (and (= oldpos (point)) (move-end-of-line nil))))\r
+\r
+(keymap-global-set "<remap> <move-end-of-line>" #'re/smart-end-of-line)\r
+(keymap-set visual-line-mode-map "C-e" #'re/smart-end-of-line)\r
+\f\r
+;;; Goto line with numbers\r
+;; I only care about line numbers when I want to jump, so only show them then\r
+\r
+(defun re/goto-line ()\r
+ "Show line numbers before going to line, then hide them again."\r
+ (interactive)\r
+ (display-line-numbers-mode 1)\r
+ (call-interactively 'goto-line)\r
+ (display-line-numbers-mode -1))\r
+\r
+(keymap-global-set "<remap> <goto-line>" #'re/goto-line)\r
+\f\r
+;;; Switching between buffers\r
+\r
+(keymap-global-set "C-." #'next-buffer)\r
+(keymap-global-set "C-," #'previous-buffer)\r
+(keymap-global-set "C-<tab>" (lambda () (interactive) (switch-to-buffer nil))) ; toggle last two buffers\r
+(keymap-global-set "<mouse-4>" #'previous-buffer) ; Back button\r
+(keymap-global-set "<mouse-5>" #'next-buffer) ; Forward button\r
+\f\r
+;;; Disable M-mouse for window manager bindings\r
+\r
+(keymap-global-unset "M-<down-mouse-1>")\r
+(keymap-global-unset "M-<drag-mouse-1>")\r
+(keymap-global-unset "M-<mouse-1>")\r
+(keymap-global-unset "M-<mouse-2>")\r
+(keymap-global-unset "M-<mouse-3>")\r
+\f\r
+;;; Search\r
+(defun re/add-region-to-search-ring ()\r
+ (when (use-region-p)\r
+ (add-to-history 'search-ring\r
+ (buffer-substring (region-beginning) (region-end)))\r
+ (deactivate-mark)))\r
+\r
+(defun re/isearch-forward-use-region ()\r
+ "Search text in region if active, otherwise normal forward search."\r
+ (interactive)\r
+ (re/add-region-to-search-ring)\r
+ (isearch-forward))\r
+\r
+(keymap-global-set "C-s" #'re/isearch-forward-use-region)\r
+\r
+(defun re/isearch-backward-use-region ()\r
+ "Search text in region if active, otherwise normal backward search."\r
+ (interactive)\r
+ (re/add-region-to-search-ring)\r
+ (isearch-backward))\r
+\r
+(keymap-global-set "C-r" #'re/isearch-backward-use-region)\r
+\r
+(keymap-global-set "M-s ." #'isearch-forward-thing-at-point)\r
+(keymap-global-set "M-s M-." #'isearch-forward-symbol-at-point)\r
+(keymap-global-set "M-%" #'replace-regexp)\r
+(keymap-global-set "C-%" #'replace-string)\r
+(keymap-global-set "C-M-S-R" #'replace-regexp-as-diff)\r
+(keymap-global-set "C-S-S" #'isearch-forward-regexp)\r
+(keymap-global-set "C-S-R" #'isearch-backward-regexp)\r
+\f\r
+;;; Recent Files/Buffers\r
+\r
+(midnight-mode t)\r
+(setq clean-buffer-list-delay-general 1)\r
+(dolist (never-re '("\\` \\*tramp/.*\\'"\r
+ "\\` \\*eshell.*\\'"))\r
+ (add-to-list 'clean-buffer-list-kill-never-regexps never-re))\r
+(save-place-mode 1)\r
+(recentf-mode 1)\r
+\f\r
+;;; File Handling\r
+;; Rename visited file\r
+\r
+(defun re/rename-current-buffer-file nil\r
+ "Renames the current buffer and the file it is visiting."\r
+ (interactive)\r
+ (let ((name (buffer-name))\r
+ (filename (buffer-file-name)))\r
+ (if (not (and filename (file-exists-p filename)))\r
+ (error "Buffer '%s' is not visiting a file!" name)\r
+ (let ((new-name (read-file-name "New name: " filename)))\r
+ (if (get-buffer new-name)\r
+ (error "A buffer named '%s' already exists!" new-name)\r
+ (rename-file filename new-name 1)\r
+ (rename-buffer new-name)\r
+ (set-visited-file-name new-name)\r
+ (set-buffer-modified-p nil)\r
+ (message "File '%s' successfully renamed to '%s'"\r
+ name\r
+ (file-name-nondirectory new-name)))))))\r
+\r
+(keymap-global-set "C-x C-r" #'re/rename-current-buffer-file)\r
+\r
+;; Copy file with datestamp appended\r
+\r
+(defun re/get-file-modified-time (filepath)\r
+ (interactive)\r
+ (format-time-string "%Y-%m-%d-%H%M"\r
+ (file-attribute-modification-time (file-attributes filepath))))\r
+\r
+(defun re/filename-inject-mod-time (filepath)\r
+ (interactive)\r
+ (concat (file-name-sans-extension filepath)\r
+ "_"\r
+ (re/get-file-modified-time filepath)\r
+ (file-name-extension filepath t)))\r
+\r
+(defun re/copy-with-mod-time (&optional filepath)\r
+ (interactive)\r
+ (let ((filepath (if (eq major-mode 'dired-mode)\r
+ (dired-get-filename t)\r
+ (when (null filepath)\r
+ (read-file-name "File to copy with datestring: "\r
+ default-directory\r
+ nil\r
+ t\r
+ nil)))))\r
+ (copy-file filepath (re/filename-inject-mod-time filepath ) nil t t t)))\r
+\f\r
+;;; Dired\r
+;; General Settings\r
+\r
+(setq dired-listing-switches "-Ahl"\r
+ dired-vc-rename-file t\r
+ dired-auto-revert-buffer t\r
+ dired-hide-details-hide-symlink-targets nil\r
+ dired-kill-when-opening-new-dired-buffer t)\r
+\r
+;; Open file next to dir buffer\r
+\r
+(defun re/dired-display-direction ()\r
+ "In dired mode, visit the file at the cursor in the right/below/left/above window."\r
+ (interactive)\r
+ (let* ((file-or-dir (dired-get-file-for-visit)) ;; get the file at cursor\r
+ (buffer (find-file-noselect file-or-dir))) ;; load the file into a buffer\r
+ (let ((window ;; figure out the window to use\r
+ (cond ((get-buffer-window buffer (selected-frame)))\r
+ ((window-in-direction 'right)) ;; try window in each direction\r
+ ((window-in-direction 'below)) ;; and default to right\r
+ ((window-in-direction 'left)) ;; if no window found.\r
+ ((window-in-direction 'above))\r
+ (t (split-window-sensibly (selected-window))))))\r
+ (window--display-buffer buffer window 'window nil)\r
+ window)))\r
+;; Open in new frame\r
+\r
+(defun re/dired-new-frame ()\r
+ "Open dired in a new frame."\r
+ (interactive)\r
+ (re/open-with-new-frame "DIRED"\r
+ #'(lambda () (interactive) (dired default-directory))))\r
+;; Multiple Files\r
+\r
+(keymap-global-set "<f8>" #'re/dired-new-frame)\r
+\r
+(defun re/dired-find-marked-files ()\r
+ "Open all marked files from `dired'."\r
+ (interactive)\r
+ (mapc #'find-file (reverse (dired-get-marked-files))))\r
+;; Open file in external program\r
+\r
+(defun re/dired-sort ()\r
+ "Change sort order of dired dir."\r
+ (interactive)\r
+ (let ((smenu '(("name" . "-Ahl")\r
+ ("mdate" . "-Ahlt")\r
+ ("adate" . "-Ahltu")\r
+ ("size" . "-AhlS")\r
+ ("dir" . "-Ahl --group-directories-first")))\r
+ ssortBy)\r
+ (setq ssortBy (completing-read "Sort by (default name):"\r
+ smenu\r
+ nil\r
+ t\r
+ nil\r
+ nil\r
+ (caar smenu)))\r
+ (dired-sort-other (cdr (assoc ssortBy smenu)))))\r
+;; From dired\r
+\r
+(defun re/open-file-external (file)\r
+ "Open a file in an external program."\r
+ (interactive)\r
+ (let ((process-connection-type nil))\r
+ (cond ((eq system-type 'windows-nt)\r
+ (w32-shell-execute "open"\r
+ (replace-regexp-in-string "/" "\\\\" file t t)))\r
+ ((eq system-type 'gnu/linux)\r
+ (start-process "" nil "xdg-open" file))\r
+ (t (error "Unable to automatically open this file")))))\r
+;; Multiple files\r
+\r
+(defun re/dired-open-externally ()\r
+ "Open the file at point in an external program."\r
+ (interactive)\r
+ (re/open-file-external (dired-get-filename)))\r
+\r
+(defun re/dired-open-marked-externally ()\r
+ "Open all marked files from `dired' in an external program."\r
+ (interactive)\r
+ (mapc #'re/open-file-external (reverse (dired-get-marked-files))))\r
+\r
+(add-hook 'dired-mode-hook\r
+ (lambda ()\r
+ (keymap-set dired-mode-map "o" #'re/dired-display-direction)\r
+ (keymap-set dired-mode-map "z" #'dired-do-compress-to)\r
+ (keymap-set dired-mode-map "c" #'dired-do-chmod)\r
+ (keymap-set dired-mode-map "r" #'dired-do-rename)\r
+ (keymap-set dired-mode-map "M" #'dired-do-rename)\r
+ (keymap-set dired-mode-map "s" #'re/dired-sort)\r
+ (keymap-set dired-mode-map "F" #'re/dired-find-marked-files)\r
+ (keymap-set dired-mode-map "M-C" #'re/copy-with-mod-time)\r
+ (keymap-set dired-mode-map "b" #'re/dired-open-externally)\r
+ (keymap-set dired-mode-map "B" #'re/dired-open-marked-externally)\r
+ (dired-hide-details-mode t)\r
+ (hl-line-mode)))\r
+\f\r
+;;; Bookmarks\r
+\r
+(setq bookmark-save-flag 1)\r
+(keymap-global-set "<f7>" #'recentf-open-files)\r
+(keymap-global-set "C-x r B" #'bookmark-jump-other-window)\r
+\f\r
+;;; GUI\r
+\r
+(load-theme 'eng-paper t)\r
+\r
+(if (equal system-type 'android)\r
+ (progn\r
+ (setq touch-screen-display-keyboard t)\r
+ (set-frame-parameter nil 'tool-bar-position 'bottom)\r
+ (modifier-bar-mode 1)\r
+ (tool-bar-mode 1)\r
+ (define-key key-translation-map\r
+ (kbd "<volume-up>")\r
+ (kbd "C-i"))\r
+ (define-key key-translation-map\r
+ (kbd "<volume-down>")\r
+ 'event-apply-meta-modifier))\r
+ (progn\r
+ (menu-bar-mode -1)\r
+ (tool-bar-mode -1)))\r
+(set-scroll-bar-mode (if (eq system-type 'windows-nt) nil 'left))\r
+\r
+(setq frame-title-format "%b"\r
+ mouse-prefer-closest-glyph t\r
+ mouse-1-click-follows-link nil\r
+ focus-follows-mouse t\r
+ mouse-autoselect-window -0.25)\r
+\r
+(setq-default cursor-type 'hbar\r
+ cursor-in-non-selected-windows nil)\r
+\f\r
+;;; Mode Line\r
+;; Remove all minor modes from the mode-line, and show /all/ of them in the\r
+;; minor mode menu. Must come before the modeline customization to ensure\r
+;; =minions-mode-line-modes= is defined.\r
+\r
+(require 'minions)\r
+(minions-mode)\r
+\r
+(setq minions-mode-line-lighter "+")\r
+\r
+;; Format\r
+\r
+(setq-default mode-line-format '(" "\r
+ (:eval (propertize "%b"\r
+ 'face\r
+ (when (buffer-modified-p)\r
+ 'mode-line-highlight)))\r
+ " "\r
+ mode-line-remote\r
+ " "\r
+ minions-mode-line-modes\r
+ (vc-mode vc-mode)\r
+ mode-line-format-right-align\r
+ (:eval (number-to-string (line-number-at-pos (point-max))))\r
+ "L/%Ib (%l,%C) "))\r
+\f\r
+;;; Pretty Symbols\r
+\r
+(setq prettify-symbols-alist '(("lambda" . 955)\r
+ ("delta" . 120517)\r
+ ("epsilon" . 120518)\r
+ ("->" . 8594)\r
+ ("<=" . 8804)\r
+ (">=" . 8805)))\r
+\r
+(global-prettify-symbols-mode t)\r
+\f\r
+;;; Parens\r
+;; Must be set after loading any theme\r
+\r
+(setq show-paren-delay 0\r
+ show-paren-style 'expression)\r
+\r
+(setq electric-pair-pairs '((?\" . ?\")\r
+ (?\â . ?\â)\r
+ (?\â . ?\â)\r
+ (?\{ . ?\})\r
+ (?\< . ?\>)))\r
+\f\r
+;;; Help\r
+\r
+(setq apropos-do-all t)\r
+\f\r
+;;; UTF-8\r
+\r
+(prefer-coding-system 'utf-8-unix)\r
+(set-language-environment 'utf-8)\r
+\f\r
+;;; Cleanup on write\r
+\r
+(defun re/flush-extra-lines ()\r
+ "Change all groups of blank lines to 1 blank line."\r
+ (interactive)\r
+ (save-excursion\r
+ (replace-regexp "^\n+"\r
+ "\n"\r
+ nil\r
+ (point-min)\r
+ (point-max))))\r
+\r
+(setq require-final-newline t)\r
+(add-hook 'before-save-hook #'re/flush-extra-lines)\r
+(add-hook 'before-save-hook #'whitespace-cleanup)\r
+(add-hook 'after-save-hook #'executable-make-buffer-file-executable-if-script-p)\r
+\f\r
+;;; Password generation\r
+\r
+(defconst *alphanumerics*\r
+ (mapcar #'char-to-string\r
+ (append (number-sequence ?a ?z)\r
+ (number-sequence ?A ?Z)\r
+ (number-sequence ?0 ?9)))\r
+ "List of strings of the upper/lower-case English alphabet, and the single-digit numbers.")\r
+\r
+(defconst *special-characters*\r
+ '("!" "?" "$" "%" "&" "'" "(" ")" "*" "+" "," "-" "."\r
+ "/" ":" ";" "<" "=" ">" "?" "@" "[" "]" "^" "_" "{"\r
+ "|" "}" "~")\r
+ "List of strings containing the special visible characters excluding \\ and \" .")\r
+\r
+(defun pw-get-char (&optional no-special-chars)\r
+ "Select a single character at random to be used in a password.\r
+If `no-special-chars' is t, use only alpha-numeric characters."\r
+ (let ((selected-charset (append *alphanumerics*\r
+ (when (null no-special-chars)\r
+ *special-characters*))))\r
+ (elt selected-charset (random (length selected-charset)))))\r
+\r
+(defun pw-get-chars (password-length &optional no-special-chars)\r
+ "Generate a list of characters as strings to be made into a password.\r
+If `no-special-chars' is t, use only alpha-numeric characters."\r
+ (append (list (pw-get-char no-special-chars))\r
+ (when (> password-length 1)\r
+ (pw-get-chars (- password-length 1) no-special-chars))))\r
+\r
+(defun generate-password (password-length &optional no-special-chars)\r
+ "Combine a list of randomly-selected characters into a single string of length given.\r
+calls `pw-get-chars'. If `no-special-chars' is `t', use only alpha-numeric characters."\r
+ (apply #'concat (pw-get-chars password-length no-special-chars)))\r
+\r
+(defun insert-generated-password (&optional password-length)\r
+ "Insert a generated password into the current buffer.\r
+Negative password-length (or negative arg) sets `no-special-chars' for `generate-password'."\r
+ (interactive "p")\r
+ (let ((len (if (or (null password-length) (= 1 (abs password-length)))\r
+ 16\r
+ (abs password-length)))\r
+ (no-special-p (and (numberp password-length) (< password-length 0))))\r
+ (insert (generate-password len no-special-p))))\r
+\r
+;; Get a password from a pass(1) entry\r
+;; Assume the password is on the first line.\r
+\r
+(defun re/get-line-from-file (file-path line-number)\r
+ "Return the specified LINE-NUMBER from FILE-PATH as a string.\r
+Start from bottom if given a negative line number."\r
+ (with-temp-buffer\r
+ (insert-file-contents file-path)\r
+ (if (< line-number 0)\r
+ (progn\r
+ (goto-char (point-max))\r
+ (forward-line line-number))\r
+ (progn\r
+ (goto-char (point-min))\r
+ (forward-line (1- line-number))))\r
+ (buffer-substring-no-properties (line-beginning-position) (line-end-position))))\r
+\r
+(defun get-password (&optional filename)\r
+ "Return a password given a file where it is stored.\r
+Assumes same structure as `pass(1)'."\r
+ (re/get-line-from-file (keyfile-string filename) 1))\r
+\r
+(defun password-to-clipboard (&optional filename)\r
+ "Copy the returned password to the clipboard."\r
+ (interactive)\r
+ (kill-new (get-password filename)))\r
+\f\r
+;;; TOTP\r
+;; Generate TOTP tokens, generate the codes, put them on the clipboard.\r
+;; Assumes the format of pass(1), where the TOTP token is the last line in the\r
+;; file.\r
+\r
+(dolist (requirement '(bindat gnutls hexl auth-source))\r
+ (require requirement))\r
+\r
+(defun totp--hex-decode-string (string)\r
+ "Hex-decode STRING and return the result as a unibyte string."\r
+ (apply #'unibyte-string\r
+ (seq-map (lambda (s) (hexl-htoi (aref s 0) (aref s 1)))\r
+ (seq-partition string 2))))\r
+\r
+(defun totp (string &optional time digits)\r
+ "Return a TOTP token using the secret hex STRING and current time.\r
+TIME is used as counter value instead of current time, if non-nil.\r
+DIGITS is the number of pin digits and defaults to 6."\r
+ (let* ((key-bytes (totp--hex-decode-string (upcase string)))\r
+ (counter (truncate (/ (or time (time-to-seconds)) 30)))\r
+ (digits (or digits 6))\r
+ (format-string (format "%%0%dd" digits))\r
+ ;; we have to manually split the 64 bit number (u64 not supported in Emacs 27.2)\r
+ (counter-bytes (bindat-pack '((:high u32) (:low u32))\r
+ `((:high . ,(ash counter -32)) (:low . ,(logand counter #xffffffff)))))\r
+ (mac (gnutls-hash-mac 'SHA1 key-bytes counter-bytes))\r
+ (offset (logand (bindat-get-field (bindat-unpack '((:offset u8)) mac 19) :offset) #xf)))\r
+ (format format-string\r
+ (mod\r
+ (logand (bindat-get-field (bindat-unpack '((:totp-pin u32)) mac offset) :totp-pin)\r
+ #x7fffffff)\r
+ (expt 10 digits)))))\r
+\r
+(defconst base32-alphabet\r
+ (let ((tbl (make-char-table nil)))\r
+ (dolist (mapping '(("A" . 0) ("B" . 1) ("C" . 2) ("D" . 3)\r
+ ("E" . 4) ("F" . 5) ("G" . 6)\r
+ ("H" . 7) ("I" . 8) ("J" . 9) ("K" . 10)\r
+ ("L" . 11) ("M" . 12) ("N" . 13)\r
+ ("O" . 14) ("P" . 15) ("Q" . 16) ("R" . 17)\r
+ ("S" . 18) ("T" . 19) ("U" . 20)\r
+ ("V" . 21) ("W" . 22) ("X" . 23) ("Y" . 24)\r
+ ("Z" . 25) ("2" . 26) ("3" . 27)\r
+ ("4" . 28) ("5" . 29) ("6" . 30) ("7" . 31)))\r
+ (aset tbl (string-to-char (car mapping)) (cdr mapping)))\r
+ tbl)\r
+ "Base-32 mapping table, as defined in RFC 4648.")\r
+\r
+(defun base32-hex-decode (string)\r
+ "The cheats' version of base-32 decode.\r
+\r
+This is not a 100% faithful implementation of RFC 4648. The\r
+concept of encoding partial quanta is not implemented fully.\r
+\r
+No attempt is made to pad the output either as that is not\r
+required for HMAC-TOTP."\r
+ (unless (mod (length string) 8)\r
+ (error "Padding is incorrect"))\r
+ (setq string (upcase string))\r
+ (let ((trimmed-array (append (string-trim-right string "=+") nil)))\r
+ (format "%X" (seq-reduce\r
+ (lambda (acc char) (+ (ash acc 5) (aref base32-alphabet char)))\r
+ trimmed-array 0))))\r
+\r
+(defun keyfile-string (filename)\r
+ "Return a string which is the filepath to a `pass(1)' file."\r
+ (let ((directory "~/.password-store/"))\r
+ (if (null filename)\r
+ (read-file-name "Select a password: " directory nil t nil)\r
+ (concat directory\r
+ (if (stringp filename)\r
+ filename\r
+ (symbol-name filename))\r
+ ".gpg"))))\r
+\r
+(defun get-totp (&optional filename)\r
+ "Generate a totp code given a file with the secret hex string.\r
+If no filename given, prompt for one.\r
+Assumes the file has the string by itself on the last line of the file,\r
+similar to what pass-otp does, but without the full uri.\r
+Also assumes the file is in `~/.password-store' and has the `.gpg' extension."\r
+ (totp (base32-hex-decode (re/get-line-from-file (keyfile-string filename) -1))))\r
+\r
+(defun otp-to-clipboard (&optional filename)\r
+ "Copy the returned totp code to the clipboard."\r
+ (interactive)\r
+ (kill-new (get-totp filename)))\r
+\f\r
+;;; Emacs Control\r
+\r
+(defun re/kill-emacs ()\r
+ "Save all open files and kill emacs."\r
+ (interactive)\r
+ (save-some-buffers t)\r
+ (kill-emacs))\r
+\r
+(keymap-global-set "C-x M-c" #'re/kill-emacs)\r
+\r
+(defun re/restart-emacs ()\r
+ "Save all open files and restart emacs."\r
+ (interactive)\r
+ (save-some-buffers t)\r
+ (kill-emacs nil t))\r
+\r
+(keymap-global-set "C-x M-r" #'re/restart-emacs)\r
+\r
+(defun re/keyboard-quit ()\r
+ "Smarter version of `keyboard-quit'.\r
+Close the minibuffer if not focused when hit.\r
+Stolen from emacsredux.com."\r
+ (interactive)\r
+ (if (active-minibuffer-window)\r
+ (if (minibufferp)\r
+ (minibuffer-keyboard-quit)\r
+ (abort-recursive-edit))\r
+ (keyboard-quit)))\r
+\r
+(keymap-global-set "<remap> <keyboard-quit>" #'re/keyboard-quit) ; Make C-g better\r
+(keymap-global-set "C-x S" (lambda () (interactive) (save-some-buffers t))) ; don't ask\r
+(keymap-global-set "C-x c" #'delete-frame) ; delete a frame without asking about saving files\r
+\f\r
+;;; Version-control mode\r
+\r
+(defun re/vc-clone ()\r
+ "Interactively clone with vc-mode.\r
+Prompt for url and local dir."\r
+ (interactive)\r
+ (let* ((url (read-string "Repository URL: "))\r
+ (dir (read-string "Local Dir: " (file-name-base url))))\r
+ (vc-git-clone url dir nil)))\r
+\r
+(keymap-global-set "C-x v C" #'re/vc-clone)\r
+\r
+(defun re/vc-show-branches (&optional arg)\r
+ "Display all Git branches in a separate buffer.\r
+Remotes as well when arg."\r
+ (interactive "P")\r
+ (let ((default-directory (if (boundp 'vc-dir-directory)\r
+ vc-dir-directory\r
+ default-directory)))\r
+ (vc-git-command "*git-branches*"\r
+ nil\r
+ nil\r
+ "branch"\r
+ "--verbose"\r
+ (when arg "--remotes"))\r
+ (pop-to-buffer "*git-branches*")\r
+ (goto-char (point-min))\r
+ (special-mode)))\r
+\r
+(keymap-global-set "C-x v b b" #'re/vc-show-branches)\r
+\r
+(defun re/vc-fetch (&optional arg)\r
+ "Interactively fetch with vc-mode.\r
+Allows separate fetch in addition to pull. Only care about git.\r
+With arg, ask for remote, otherwise fetch all."\r
+ (interactive "P")\r
+ (let* ((default-directory (if (boundp 'vc-dir-directory)\r
+ vc-dir-directory\r
+ default-directory)))\r
+ (vc-git-command "*vc-fetch*"\r
+ nil\r
+ nil\r
+ "fetch"\r
+ (if arg\r
+ (read-string "Fetch From: " "origin")\r
+ "--all")\r
+ "--verbose")\r
+ (pop-to-buffer "*vc-fetch*")\r
+ (goto-char (point-min))\r
+ (special-mode)))\r
+\r
+(keymap-global-set "C-x v f" #'re/vc-fetch)\r
+\r
+(add-hook 'vc-dir-mode-hook\r
+ (lambda ()\r
+ (keymap-set vc-dir-mode-map "b b" #'re/vc-show-branches)\r
+ (keymap-set vc-dir-mode-map "f" #'re/vc-fetch)\r
+ (keymap-set vc-dir-mode-map "k" #'vc-revert)))\r
+\r
+(setq vc-make-backup-files nil\r
+ vc-handled-backends '(jj Git)\r
+ vc-git-log-switches '("--oneline" "--graph" "--decorate" "--all")\r
+ vc-git-log-edit-summary-target-len 50\r
+ vc-find-revision-no-save t)\r
+\f\r
+;;; Backups\r
+\r
+(setq backup-directory-alist `((".*" . "~/emacs-backups/"))\r
+ backup-by-copying t\r
+ delete-old-versions t\r
+ kept-new-versions 5\r
+ kept-old-versions 5)\r
+\f\r
+;;; Any Lisp Repl\r
+\r
+(defmacro re/send-on-close-paren (executor)\r
+ "Generalizes sending an execute on close paren.\r
+ Interactively call `executor'. For `executor', use whatever\r
+ function is called by `<RET>' in the applicable REPL. For a\r
+ non-repl, use `newline' or similar."\r
+ `(lambda (&optional arg)\r
+ (interactive "p")\r
+ (insert-char 41 arg)\r
+ (call-interactively ,executor)))\r
+\r
+(defun re/sexp-drop-paren-p ()\r
+ "Returns t if sexp needs fewer parens to balance."\r
+ (< (car (syntax-ppss)) 0))\r
+\r
+(defun re/sexp-need-paren-p ()\r
+ "Returns t if sexp needs more parens to balance."\r
+ (> (car (syntax-ppss)) 0))\r
+\r
+(defun re/sexp-unbalanced-count ()\r
+ "Returns distance from balanced sexp."\r
+ (abs (car (syntax-ppss))))\r
+\r
+(defun re/balance-sexp ()\r
+ "Balance the sexp before point.\r
+Either delete, move forward, or add as many ')' as needed."\r
+ (let ((distance (re/sexp-unbalanced-count)))\r
+ (cond ((re/sexp-need-paren-p)\r
+ (if (looking-at ")")\r
+ (progn\r
+ (forward-char)\r
+ (re/balance-sexp))\r
+ (insert-char 41 distance)))\r
+ ((re/sexp-drop-paren-p)\r
+ (backward-delete-char distance)))))\r
+\r
+(defmacro re/balance-and-eval-sexp (executor)\r
+ "Balance the sexp before point, then call `executor'.\r
+Call `re/balance-sexp', then interactively call `executor'. Bind\r
+this to ']' for the interlisp experience. For `executor', use\r
+whatever function is called by `<RET>' in the applicable REPL.\r
+For a non-repl, use `newline' or similar."\r
+ `(lambda () (interactive) (re/balance-sexp) (call-interactively ,executor)))\r
+\r
+(defvar *re/lisp-mode-hooks*\r
+ '(emacs-lisp-mode-hook\r
+ lisp-mode-hook\r
+ sly-mode-hook\r
+ scheme-mode-hook\r
+ geiser-mode-hook\r
+ lisp-interaction-mode-hook))\r
+\r
+(dolist (hook *re/lisp-mode-hooks*)\r
+ (add-hook hook\r
+ (lambda ()\r
+ (when (bound-and-true-p acme-mouse-mode) (acme-mouse-mode -1))\r
+ (sedit-mouse-mode 1)\r
+ (keymap-set lisp-mode-shared-map "C-M-z" #'eval-region)\r
+ (keymap-set lisp-mode-shared-map\r
+ "C-M-S-q"\r
+ #'sedit-auto-prettify-sexp)\r
+ (keymap-set lisp-mode-shared-map\r
+ "]"\r
+ (re/balance-and-eval-sexp #'sedit-auto-prettify-sexp))\r
+ (keymap-set lisp-interaction-mode-map\r
+ "]"\r
+ (re/balance-and-eval-sexp #'eval-print-last-sexp)))))\r
+\f\r
+;;; IELM\r
+;; Rarely used, but nice to have when I want it.\r
+\r
+(add-hook 'ielm-mode-hook\r
+ (lambda ()\r
+ (keymap-set inferior-emacs-lisp-mode-map\r
+ "C-l"\r
+ #'comint-clear-buffer)\r
+ (let ((eval-function #'ielm-send-input))\r
+ (keymap-set inferior-emacs-lisp-mode-map\r
+ ")"\r
+ (re/send-on-close-paren eval-function))\r
+ (keymap-set inferior-emacs-lisp-mode-map\r
+ "]"\r
+ (re/balance-and-eval-sexp eval-function)))))\r
+\f\r
+;;; Eshell\r
+\r
+(keymap-global-set "C-c e" #'eshell)\r
+\r
+(defun eshell-quit-or-delete-char (arg)\r
+ "Delete char if at one, quit eshell if on empty prompt.\r
+Stolen from https://depp.brause.cc/dotemacs"\r
+ (interactive "p")\r
+ (if (and (eolp) (looking-back eshell-prompt-regexp 0 t))\r
+ (eshell-life-is-too-much)\r
+ (delete-char arg)))\r
+\r
+(add-hook 'eshell-mode-hook\r
+ (lambda ()\r
+ (keymap-set eshell-mode-map "C-d" #'eshell-quit-or-delete-char)\r
+ (let ((eval-function #'eshell-send-input))\r
+ (keymap-set eshell-mode-map\r
+ ")"\r
+ (re/send-on-close-paren eval-function))\r
+ (keymap-set eshell-mode-map\r
+ "]"\r
+ (re/balance-and-eval-sexp eval-function)))))\r
+\f\r
+;;; C-mode\r
+\r
+(dolist (mode-maps '(("\\.keymap\\'" . c-mode) ("\\.dtsi\\'" . c-mode)))\r
+ (add-to-list 'auto-mode-alist mode-maps t))\r
+\r
+(defun re/c-lineup-arglist-tabs-only (ignored)\r
+ "Line up argument lists by tabs, not spaces.\r
+ Stolen from https://kernel.org/doc/html/v4.10/process/coding-style.html"\r
+ (let* ((anchor (c-langelem-pos c-syntactic-element))\r
+ (column (c-langelem-2nd-pos c-syntactic-element))\r
+ (offset (- (1+ column) anchor))\r
+ (steps (floor offset c-basic-offset)))\r
+ (* (max steps 1) c-basic-offset)))\r
+\r
+(add-hook 'c-mode-common-hook\r
+ (lambda ()\r
+ (c-add-style "linux-tabs-only"\r
+ '("linux" (c-offsets-alist\r
+ (arglist-cont-nonempty\r
+ c-lineup-gcc-asm-reg\r
+ re/c-lineup-arglist-tabs-only))))))\r
+\r
+(add-hook 'c-mode-hook\r
+ (lambda ()\r
+ (setq c-backspace-function 'backward-delete-char)\r
+ (c-set-style "linux-tabs-only")))\r
+\f\r
+;;; Auto-Revert\r
+\r
+(global-auto-revert-mode)\r
+(setq global-auto-revert-non-file-buffers nil)\r
+\f\r
+;;; Crash HandliĆ\r
+\r
+(setq attempt-stack-overflow-recovery nil\r
+ attempt-orderly-shutdown-on-fatal-signal nil)\r
+\f\r
+;;; Abbrev-mode and skeletons\r
+\r
+(define-skeleton workorder-link\r
+ "part of a link to a workorder, just enter the number."\r
+ ""\r
+ "https://eservice.r717.net/index.php/PLC_workorders/store/"\r
+ _ -)\r
+\r
+(define-skeleton skel-org-block\r
+ "Add a source block to an org file."\r
+ ""\r
+ "#+BEGIN_SRC"\r
+ \n\r
+ _ -\r
+ \n\r
+ "#+END_SRC")\r
+\r
+(define-skeleton skel-org-block-elisp\r
+ "Add an elisp source block to an org file."\r
+ ""\r
+ "#+BEGIN_SRC emacs-lisp"\r
+ \n\r
+ _ -\r
+ \n\r
+ "#+END_SRC")\r
+\r
+(setq save-abbrevs 'silently)\r
+\r
+(add-hook 'org-mode-hook #'abbrev-mode)\r
+\f\r
+;;; Customization Framework\r
+;; Disable it by making it save to a temp file (stolen from prot)\r
+\r
+(setq custom-file (make-temp-file "emacs-custom-"))\r