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