From cc3bc245898c20defe63a8e19f8def901a5f99c5 Mon Sep 17 00:00:00 2001 From: rearman Date: Fri, 8 May 2026 15:15:09 -0400 Subject: [PATCH] Move back to literate config Restless config syndrome strikes again... Better handling now, since I know how to keep it from tangling on every start, AND I have a better idea about jumping back to the org-file than I did previously. I also need to do that less and less. --- README.org | 941 +++++++++++++++++++++++++++++++++++++++++++++++++++++ init.el | 864 +----------------------------------------------- 2 files changed, 947 insertions(+), 858 deletions(-) create mode 100644 README.org diff --git a/README.org b/README.org new file mode 100644 index 0000000..1511153 --- /dev/null +++ b/README.org @@ -0,0 +1,941 @@ +#+TITLE: Emacs literate config +#+PROPERTY: header-args emacs-lisp :tangle config.el + +* Header +#+BEGIN_SRC emacs-lisp +;;; config.el --- Emacs Configuration -*- lexical-binding: t; -*- + +;; This file is not part of GNU emacs + +;;; Code: + +#+END_SRC +* Initial +#+BEGIN_SRC emacs-lisp + (dolist (path '("sedit-mouse/" "themes/" "re/")) + (add-to-list 'load-path (concat user-emacs-directory path))) + + (dolist (file '(re-packages re-pass-alike eng-paper-theme sedit-mouse)) + (require file)) + + (setq w32-recognize-altgr 'nil) + + (unless (equal system-type 'windows-nt) + (require 're-mu4e) + (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 +* Editing +#+BEGIN_SRC emacs-lisp +;; Editing +(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/flush-extra-lines () + "Change all groups of blank lines to 1 blank line." + (interactive) + (save-excursion (mark-whole-buffer) + (replace-regexp "^\n+" + "\n" + nil + (region-beginning) + (region-end)))) + +(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) + +(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 "" 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-u" (lambda () (interactive) (kill-line 0))) ; kill-line backwards +(keymap-global-set "C-S-U" #'universal-argument) ; C-u used above +(keymap-set key-translation-map "C-h" "") ; Rather F1 for help, also C-S-H works +(keymap-global-set "C-S-d" #'delete-pair) +(keymap-global-set "M-j" (lambda () (interactive) (join-line 0))) ; join-line other way +(keymap-global-set "C-z" #'zap-up-to-char) +(keymap-global-set "C-c s" #'sort-lines) +(keymap-global-set " " #'capitalize-dwim) ; M-c +(keymap-global-set " " #'downcase-dwim) ; M-l +(keymap-global-set " " #'upcase-dwim) ; M-u +#+END_SRC +* 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 "" (lambda () (interactive) (insert "\t"))) +#+END_SRC +* Buffer dealiŋ +#+BEGIN_SRC emacs-lisp +;; Buffer dealiŋ +(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)) + +(add-to-list 'display-buffer-alist + '("^\\*eldoc" display-buffer-in-direction + (direction . 'down) + (window-height . 4))) + +(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 + 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 "" #'forward-page) +(keymap-global-set "" #'backward-page) + +(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))) + +(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) +(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 +* Motion +#+BEGIN_SRC emacs-lisp +;; Motion +(defun re/scratch-only () + "Bring up the scratch buffer as the only visible buffer." + (interactive) + (scratch-buffer) + (delete-other-windows)) + +(keymap-global-set "" #'scratch-buffer) +(keymap-global-set "S-" #'re/scratch-only) + +(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) + +(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 " " #'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 " " #'re/smart-end-of-line) +(keymap-set visual-line-mode-map "C-e" #'re/smart-end-of-line) + +(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 " " #'re/goto-line) +(keymap-global-set "" #'beginning-of-buffer) +(keymap-global-set "" #'end-of-buffer) +(keymap-global-set "C-." #'next-buffer) +(keymap-global-set "C-," #'previous-buffer) +(keymap-global-set "C-" (lambda () (interactive) (switch-to-buffer nil))) ; toggle last two buffers +(keymap-global-set "" #'previous-buffer) ; Back button +(keymap-global-unset "M-") +(keymap-global-unset "M-") +(keymap-global-unset "M-") +(keymap-global-unset "M-") +(keymap-global-unset "M-") +#+END_SRC +* Search +#+BEGIN_SRC emacs-lisp +;; Search +(defun re/isearch-forward-use-region () + "Search text in region if active, otherwise normal forward search." + (interactive) + (when (use-region-p) + (add-to-history 'search-ring + (buffer-substring (region-beginning) (region-end))) + (deactivate-mark)) + (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) + (when (use-region-p) + (add-to-history 'search-ring + (buffer-substring (region-beginning) (region-end))) + (deactivate-mark)) + (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 " " #'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 +;; Recent Files/Buffers +(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 +;; File Handling +(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-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 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 +;; Dired +(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 (selected-window) nil 'right))))) + (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 "" #'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 () + (progn + (keymap-set dired-mode-map + "o" + #'re/dired-display-direction) + ;; Rename r,R+M(mv), chmod M>c, compress-to c>z + (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) + ;; My add-on defuns + (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 +;; Bookmarks +(setq bookmark-save-flag 1) +(keymap-global-set "" #'recentf-open-files) +(keymap-global-set "C-x r B" #'bookmark-jump-other-window) +#+END_SRC +* GUI +#+BEGIN_SRC emacs-lisp +;; GUI Settings +(load-theme 'eng-paper t) +(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) + (vc-mode vc-mode) + mode-line-format-right-align + (:eval (number-to-string (line-number-at-pos (point-max)))) + "L @ %I%n %l,%C ")) + +(setq prettify-symbols-alist '(("lambda" . 955) + ("delta" . 120517) + ("epsilon" . 120518) + ("->" . 8594) + ("<=" . 8804) + (">=" . 8805))) + +(global-prettify-symbols-mode t) +#+END_SRC +* Parens +#+BEGIN_SRC emacs-lisp +;; Parens (must be after loading the theme) +(setq show-paren-delay 0 + show-paren-style 'expression) + +(setq electric-pair-pairs '((?\" . ?\") + (?\‘ . ?\’) + (?\“ . ?\”) + (?\{ . ?\}) + (?\< . ?\>))) +#+END_SRC +* Help +#+BEGIN_SRC emacs-lisp +;; Help +(setq apropos-do-all t) +#+END_SRC +* UTF-8 +#+BEGIN_SRC emacs-lisp +;; UTF-8 +(set-charset-priority 'unicode) +(prefer-coding-system 'utf-8-unix) +(set-language-environment 'utf-8) +(set-default-coding-systems 'utf-8) +(set-keyboard-coding-system 'utf-8-unix) +(set-terminal-coding-system 'utf-8-unix) +#+END_SRC +* Cleanup on write +#+BEGIN_SRC emacs-lisp +;; Cleanup on write +(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 +* Control +#+BEGIN_SRC emacs-lisp +;; Control +(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 " " #'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 +* Versioning and Backups +#+BEGIN_SRC emacs-lisp +;; Versioning and Backups +(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))) + +(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))) + +(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))) + +(setq vc-make-backup-files nil + vc-handled-backends '(Git) + vc-git-diff-switches '("--patch-with-stat" "--histogram") + vc-git-log-switches '("--oneline" "--all" "--graph") + vc-git-log-edit-summary-target-len 50 + vc-git-log-edit-summary-max-len 70 + vc-find-revision-no-save t) + +(setq vc-git-root-log-format + `("%d %h %ai %an: %s" + ;; The first shy group matches the characters drawn by --graph. + ;; We use numbered groups because `log-view-message-re' wants the + ;; revision number to be group 1. + ,(concat "^\\(?:[*/\\|]+\\)\\(?:[*/\\| ]+\\)?" + "\\(?2: ([^)]+) \\)?\\(?1:[0-9a-z]+\\) " + "\\(?4:[0-9]\\{4\\}-[0-9-]\\{4\\}[0-9\s+:-]\\{16\\}\\) " + "\\(?3:.*?\\):") + ((1 'log-view-message) + (2 'change-log-list nil lax) + (3 'change-log-name) + (4 'change-log-date)))) + +(setq backup-directory-alist `((".*" . "~/emacs-backups/")) + backup-by-copying t + delete-old-versions t + kept-new-versions 5 + kept-old-versions 5) + +(keymap-global-set "C-x v C" #'re/vc-clone) +(keymap-global-set "C-x v b b" #'re/vc-show-branches) +(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))) +#+END_SRC +* Any Lisp Repl +#+BEGIN_SRC emacs-lisp +;; Any Lisp Repl +(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 `' 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 `' 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 +#+BEGIN_SRC emacs-lisp +;; IELM +(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 +;; Eshell +(keymap-global-set "C-c e" #'eshell) + +(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))))) + +(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))) +#+END_SRC +* SLY REPL +#+BEGIN_SRC emacs-lisp +;; SLY REPL +(add-hook 'sly-mrepl-mode-hook + (lambda () + (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))) + +(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)) +#+END_SRC +* GEISER REPL +#+BEGIN_SRC emacs-lisp +;; GEISER REPL +(add-hook 'geiser-repl-mode-hook + (lambda () + (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 +* C-mode +#+BEGIN_SRC emacs-lisp +;;; C-mode +(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 +;; Auto-Revert +(global-auto-revert-mode t) +(setq global-auto-revert-non-file-buffers t + global-auto-revert-ignore-modes '(Buffer-menu-mode)) +#+END_SRC +* Crash Handliŋ +#+BEGIN_SRC emacs-lisp +;; Crash handliŋ +(setq attempt-stack-overflow-recovery nil + attempt-orderly-shutdown-on-fatal-signal nil) +#+END_SRC +* Abbrev-mode and skeletons +#+BEGIN_SRC emacs-lisp +;; Abbrev-mode and Skeletons +(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 an elisp 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 abbrev-file-name (expand-file-name "abbrev_defs" user-emacs-directory) + save-abbrevs 'silently) + +(dolist (hook '(org-mode-hook howm-mode-hook)) + (add-hook hook #'abbrev-mode)) +#+END_SRC +* Customization Framework +#+BEGIN_SRC emacs-lisp +;; Customization framework +;; Disable by making a temp file (stolen from prot) +(setq custom-file (make-temp-file "emacs-custom-")) + +;;; config.el ends here +#+END_SRC diff --git a/init.el b/init.el index f0822dc..237b528 100644 --- a/init.el +++ b/init.el @@ -4,863 +4,11 @@ ;;; Code: -(dolist (path '("sedit-mouse/" "themes/" "re/")) - (add-to-list 'load-path (concat user-emacs-directory path))) - -(dolist (file '(re-packages re-pass-alike eng-paper-theme sedit-mouse)) - (require file)) - -(setq w32-recognize-altgr 'nil) - -(unless (equal system-type 'windows-nt) - (require 're-mu4e) - (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))))) - -;; Editing -(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/flush-extra-lines () - "Change all groups of blank lines to 1 blank line." - (interactive) - (save-excursion (mark-whole-buffer) - (replace-regexp "^\n+" - "\n" - nil - (region-beginning) - (region-end)))) - -(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) - -(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 "" 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-u" (lambda () (interactive) (kill-line 0))) ; kill-line backwards -(keymap-global-set "C-S-U" #'universal-argument) ; C-u used above -(keymap-set key-translation-map "C-h" "") ; Rather F1 for help, also C-S-H works -(keymap-global-set "C-S-d" #'delete-pair) -(keymap-global-set "M-j" (lambda () (interactive) (join-line 0))) ; join-line other way -(keymap-global-set "C-z" #'zap-up-to-char) -(keymap-global-set "C-c s" #'sort-lines) -(keymap-global-set " " #'capitalize-dwim) ; M-c -(keymap-global-set " " #'downcase-dwim) ; M-l -(keymap-global-set " " #'upcase-dwim) ; M-u - -;; 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 "" (lambda () (interactive) (insert "\t"))) - -;; Buffer dealiŋ -(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)) - -(add-to-list 'display-buffer-alist - '("^\\*eldoc" display-buffer-in-direction - (direction . 'down) - (window-height . 4))) - -(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 - 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 "" #'forward-page) -(keymap-global-set "" #'backward-page) - -(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))) - -(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) -(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 - -;; Motion -(defun re/scratch-only () - "Bring up the scratch buffer as the only visible buffer." - (interactive) - (scratch-buffer) - (delete-other-windows)) - -(keymap-global-set "" #'scratch-buffer) -(keymap-global-set "S-" #'re/scratch-only) - -(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) - -(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 " " #'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 " " #'re/smart-end-of-line) -(keymap-set visual-line-mode-map "C-e" #'re/smart-end-of-line) - -(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 " " #'re/goto-line) -(keymap-global-set "" #'beginning-of-buffer) -(keymap-global-set "" #'end-of-buffer) -(keymap-global-set "C-." #'next-buffer) -(keymap-global-set "C-," #'previous-buffer) -(keymap-global-set "C-" (lambda () (interactive) (switch-to-buffer nil))) ; toggle last two buffers -(keymap-global-set "" #'previous-buffer) ; Back button -(keymap-global-unset "M-") -(keymap-global-unset "M-") -(keymap-global-unset "M-") -(keymap-global-unset "M-") -(keymap-global-unset "M-") - -;; Search -(defun re/isearch-forward-use-region () - "Search text in region if active, otherwise normal forward search." - (interactive) - (when (use-region-p) - (add-to-history 'search-ring - (buffer-substring (region-beginning) (region-end))) - (deactivate-mark)) - (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) - (when (use-region-p) - (add-to-history 'search-ring - (buffer-substring (region-beginning) (region-end))) - (deactivate-mark)) - (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 " " #'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) - -;; Recent Files/Buffers -(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) - -;; File Handling -(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-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 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))) - -;; Dired -(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 (selected-window) nil 'right))))) - (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 "" #'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 () - (progn - (keymap-set dired-mode-map - "o" - #'re/dired-display-direction) - ;; Rename r,R+M(mv), chmod M>c, compress-to c>z - (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) - ;; My add-on defuns - (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)))) - -;; Bookmarks -(setq bookmark-save-flag 1) -(keymap-global-set "" #'recentf-open-files) -(keymap-global-set "C-x r B" #'bookmark-jump-other-window) - -;; GUI Settings -(load-theme 'eng-paper t) -(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) - (vc-mode vc-mode) - mode-line-format-right-align - (:eval (number-to-string (line-number-at-pos (point-max)))) - "L @ %I%n %l,%C ")) - -(setq prettify-symbols-alist '(("lambda" . 955) - ("delta" . 120517) - ("epsilon" . 120518) - ("->" . 8594) - ("<=" . 8804) - (">=" . 8805))) - -(global-prettify-symbols-mode t) - -;; Parens (must be after loading the theme) -(setq show-paren-delay 0 - show-paren-style 'expression) - -(setq electric-pair-pairs '((?\" . ?\") - (?\‘ . ?\’) - (?\“ . ?\”) - (?\{ . ?\}) - (?\< . ?\>))) - -;; Help -(setq apropos-do-all t) - -;; UTF-8 -(set-charset-priority 'unicode) -(prefer-coding-system 'utf-8-unix) -(set-language-environment 'utf-8) -(set-default-coding-systems 'utf-8) -(set-keyboard-coding-system 'utf-8-unix) -(set-terminal-coding-system 'utf-8-unix) - -;; Cleanup on write -(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) - -;; Control -(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 " " #'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 - -;; Versioning and Backups -(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))) - -(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))) - -(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))) - -(setq vc-make-backup-files nil - vc-handled-backends '(Git) - vc-git-diff-switches '("--patch-with-stat" "--histogram") - vc-git-log-switches '("--oneline" "--all" "--graph") - vc-git-log-edit-summary-target-len 50 - vc-git-log-edit-summary-max-len 70 - vc-find-revision-no-save t) - -(setq vc-git-root-log-format - `("%d %h %ai %an: %s" - ;; The first shy group matches the characters drawn by --graph. - ;; We use numbered groups because `log-view-message-re' wants the - ;; revision number to be group 1. - ,(concat "^\\(?:[*/\\|]+\\)\\(?:[*/\\| ]+\\)?" - "\\(?2: ([^)]+) \\)?\\(?1:[0-9a-z]+\\) " - "\\(?4:[0-9]\\{4\\}-[0-9-]\\{4\\}[0-9\s+:-]\\{16\\}\\) " - "\\(?3:.*?\\):") - ((1 'log-view-message) - (2 'change-log-list nil lax) - (3 'change-log-name) - (4 'change-log-date)))) - -(setq backup-directory-alist `((".*" . "~/emacs-backups/")) - backup-by-copying t - delete-old-versions t - kept-new-versions 5 - kept-old-versions 5) - -(keymap-global-set "C-x v C" #'re/vc-clone) -(keymap-global-set "C-x v b b" #'re/vc-show-branches) -(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))) - - -;; Any Lisp Repl -(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 `' 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 `' 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))))) - - -;; IELM - -(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))))) - -;; Eshell -(keymap-global-set "C-c e" #'eshell) - -(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))))) - -(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))) - -;; SLY REPL -(add-hook 'sly-mrepl-mode-hook - (lambda () - (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))) - -(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)) - -;; GEISER REPL -(add-hook 'geiser-repl-mode-hook - (lambda () - (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)))) - -;;; C-mode -(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"))) - -;; Auto-Revert -(global-auto-revert-mode t) -(setq global-auto-revert-non-file-buffers t - global-auto-revert-ignore-modes '(Buffer-menu-mode)) - -;; Crash handliŋ -(setq attempt-stack-overflow-recovery nil - attempt-orderly-shutdown-on-fatal-signal nil) - -;; Abbrev-mode and Skeletons -(define-skeleton workorder-link - "part of a link to a workorder, just enter the number." - "" - "https://eservice.r717.net/index.php/PLC_workorders/store/" - _ -) - -(setq abbrev-file-name (expand-file-name "abbrev_defs" user-emacs-directory) - save-abbrevs 'silently) - -(dolist (hook '(org-mode-hook howm-mode-hook)) - (add-hook hook #'abbrev-mode)) - -;; Customization framework -;; Disable by making a temp file (stolen from prot) -(setq custom-file (make-temp-file "emacs-custom-")) +(let ((org-file "~/.emacs.d/README.org") + (el-file "~/.emacs.d/config.el")) + (when (file-newer-than-file-p org-file el-file) + (require 'ob-tangle) + (org-babel-tangle-file org-file el-file)) + (load-file el-file)) ;;; init.el ends here -- 2.39.5