From: rearman Date: Mon, 11 May 2026 13:13:08 +0000 (-0400) Subject: Switch to config.org instead of README X-Git-Url: https://git.earman.xyz/?a=commitdiff_plain;h=e26c0910c430cf580fd6dd303883464faa83c5af;p=emacsinit.git Switch to config.org instead of README Much simpler when the org file and intended el file have the same name. Makes it so you can use the built-in org-babel-load-file, which checks for write-time differences before tangling. I also don't need a README for my own configs, I know where everything is. --- diff --git a/README.org b/README.org deleted file mode 100644 index efdd69e..0000000 --- a/README.org +++ /dev/null @@ -1,1362 +0,0 @@ -#+TITLE: Emacs literate config -#+STARTUP: noindent -#+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 -* Load libraries -Libraries Not from the package manager -#+BEGIN_SRC emacs-lisp - (dolist (path '("sedit-mouse/" "themes/" "re/")) - (add-to-list 'load-path (concat user-emacs-directory path))) - - (dolist (file '(pass-alike eng-paper-theme sedit-mouse)) - (require file)) -#+END_SRC -* OS specific stuff -#+BEGIN_SRC emacs-lisp - (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 -* Add melpa, and reduce the declarations by setting defaults -#+BEGIN_SRC emacs-lisp -(require 'package) - -(add-to-list 'package-archives '("melpa" . "https://melpa.org/packages/") t) - -(setq use-package-always-ensure t - use-package-always-defer t) -#+END_SRC -* Corfu -#+BEGIN_SRC emacs-lisp -(use-package corfu - :demand - t - :custom - (corfu-auto t) - (corfu-auto-delay 0) - (corfu-auto-prefix 1) - :config - (global-corfu-mode)) - -(use-package corfu-popupinfo - :ensure - nil ; part of corfu - :after - corfu - :hook - (corfu-mode . corfu-popupinfo-mode) - :custom - (corfu-popupinfo-delay '(1.0 . 0.01)) ; start after one second, fast refresh once on - (corfu-popupinfo-hide nil) - :config - (corfu-popupinfo-mode)) -#+END_SRC -* Expand Region -#+BEGIN_SRC emacs-lisp -(use-package expand-region - :bind - (("C-=" . er/expand-region))) -#+END_SRC -* Aggressive Indent -#+BEGIN_SRC emacs-lisp -(use-package aggressive-indent) -#+END_SRC -* Magit -No longer needed, since I defer by default. -* Page Break Lines -Needs to be demanded so I don't have to manually load it -#+BEGIN_SRC emacs-lisp -(use-package page-break-lines - :demand - t - :config - (global-page-break-lines-mode)) -#+END_SRC -* AucTeX and pdftools -Make AucTeX use pdftools, prompt for master file, synctex, etc. Be sure to run =pdf-tools-install= before first use. -#+BEGIN_SRC emacs-lisp -(use-package pdf-tools) - -(use-package auctex - :config - (setq-default TeX-master nil) - (add-to-list 'TeX-view-program-selection '(output-pdf "PDF Tools")) - (setq TeX-auto-save t - TeX-parse-self t - TeX-PDF-mode t - 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 -(use-package csv-mode - :config - (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 -(use-package markdown-mode - :config - (setq markdown-special-ctrl-a/e t) - :bind - (:map markdown-mode-map - ("M-" . markdown-demote) - ("M-" . markdown-promote) - ("M-" . markdown-move-up) - ("M-" . markdown-move-down) - ("C-M-u" . markdown-up-heading) - ("C-M-b" . markdown-outline-previous-same-level) - ("C-M-f" . markdown-outline-next-same-level) - ("C-M-p" . markdown-outline-previous) - ("C-M-n" . markdown-outline-next))) - -(use-package edit-indirect :after 'markdown-mode) -#+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)) - - (use-package sly - :config - (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)))))) - :bind - (:map sly-mrepl-mode-map - (")" . (lambda () (interactive) (re/send-on-close-paren #'sly-mrepl-return))) - ("]" . (lambda () (interactive) (re/balance-and-eval-sexp #'sly-mrepl-return))) - ("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 - (use-package geiser - :config - (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) - :bind - (:map geiser-repl-mode-map - (")" . (lambda () (interactive) (re/send-on-close-paren #'geiser-repl-maybe-send))) - ("]" . (lambda () (interactive) (re/balance-and-eval-sexp #'geiser-repl-maybe-send))))) - - (use-package geiser-chicken :after 'geiser) -#+END_SRC -* Forth -** TODO Try out this mode for gforth -#+BEGIN_SRC emacs-lisp -(use-package forth-mode) -#+END_SRC -* ORG -** Load in the package and set bindings -#+BEGIN_SRC emacs-lisp - (use-package org - :config - (require 'org-mouse) - :bind - (("C-c l" . org-store-link) - ("C-c a" . org-agenda) - ("C-c c" . org-capture) - ("C-c b" . org-switchb) - ("" . org-clock-out) - ("" . org-clock-goto) - :map org-mode-map - ("M-" . org-shiftmetaright) - ("M-" . org-shiftmetaleft) - ("M-S-" . org-metaright) - ("M-S-" . org-metaleft) - ("C-M-h" . org-mark-subtree) - ("C-M-u" . org-up-element) - ("C-M-d" . org-down-element) - ("C-M-b" . org-backward-heading-same-level) - ("C-M-f" . org-forward-heading-same-level) - ("C-M-p" . org-previous-visible-heading) - ("C-M-n" . org-next-visible-heading) - ("" . org-clock-in))) - - (use-package org-transclusion - :after - org - :bind - (("C-c t" . org-transclusion-mode))) -#+END_SRC -** Configuration -*** General options -#+BEGIN_SRC emacs-lisp - (with-eval-after-load 'org-mode (add-to-list 'org-export-backends 'md)) - - (setq org-startup-indented nil - org-indent-indentation-per-level 1 - org-startup-folded 'fold - org-startup-with-inline-images t - org-cycle-inline-images-display t - org-cycle-separator-lines 0 - org-blank-before-new-entry '((heading) (plain-list-item . auto)) - org-M-RET-may-split-line nil - org-treat-S-cursor-todo-selection-as-state-change nil - org-treat-insert-todo-heading-as-state-change nil - org-table-export-default-format "orgtbl-to-csv" - org-list-allow-alphabetical t - org-fold-catch-invisible-edits 'show-and-error - org-ellipsis "↴" - org-tags-column 0 - org-reverse-note-order t - org-enforce-todo-dependencies t - org-enforce-todo-checkbox-dependencies t - org-fontify-done-headline nil - org-fontify-todo-headline nil - org-goto-interface 'outline-path-completion - org-outline-path-complete-in-steps nil - org-goto-auto-isearch nil - org-special-ctrl-a/e t - org-special-ctrl-k t - org-return-follows-link t - org-yank-adjusted-subtrees t - org-src-window-setup 'current-window - org-src-preserve-indentation t) -#+END_SRC -*** Time Clocking -#+BEGIN_SRC emacs-lisp -(org-clock-persistence-insinuate) -(setq org-clock-history-length 10 - 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 -#+BEGIN_SRC emacs-lisp -(add-hook 'org-agenda-mode-hook #'hl-line-mode) - -(setq org-agenda-files (append '("~/org/") - (file-expand-wildcards "~/projects/*/")) - 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 '(("call" . ?c) - ("email" . ?e) - ("errand" . ?r) - ("jobpc" . ?j) - ("license" . ?l) - ("meeting" . ?m) - ("open" . ?o)) - org-tags-exclude-from-inheritance '("open") - 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) - ("MEETING" :foreground "#315e9a" :weight bold)) - org-agenda-restore-windows-after-quit t - org-agenda-start-on-weekday nil - org-agenda-compact-blocks t - org-agenda-span 'day - org-deadline-warning-days 30 - org-agenda-window-setup 'current-window - org-agenda-time-grid '((daily today remove-match) - (600 700 800 900 1000 1100 1200 1300 1400 1500 1600 1700 1800 1900) - " ――――― " - "") - org-agenda-tags-column 50 - org-agenda-skip-additional-timestamps-same-entry t - org-agenda-skip-deadline-if-done t - org-agenda-skip-scheduled-if-done t - org-agenda-prefix-format '((agenda . " %?-12t% s") - (todo . " ") - (tags . " ") - (search . " ")) - org-agenda-scheduled-leaders '("BOOKED: " "Booked %2d d. ago: ") - org-agenda-deadline-leaders '("DEADLINE: " "In %3d d.: " "%2d d. ago: ") - org-read-date-prefer-future 'time - org-agenda-custom-commands '(("f" "Maybe Someday" tags "someday" ((org-agenda-overriding-header "SOMEDAY:"))) - ("i" "Inbox" tags "inbox" ((org-agenda-overriding-header "INBOX:"))) - ("l" "Licenses" tags "license" ((org-agenda-overriding-header "LICENSES:"))) - ("n" "Next Actions" tags-todo "-recurring/TODO" ((org-agenda-overriding-header "NEXT:"))) - ("p" "JobPCs" tags "jobpc" ((org-agenda-overriding-header "JOB PCS:"))) - ("u" "Undertakings" tags "open" ((org-agenda-overriding-header "UNDERTAKINGS:"))) - ("w" "Waiting" todo "WAITING" ((org-agenda-overriding-header "WAITING:"))) - ("o" "Overview" ((agenda "") - (tags "inbox" ((org-agenda-overriding-header "\nINBOX:"))) - (tags-todo "-recurring/TODO" ((org-agenda-overriding-header "\nNEXT:") - (org-agenda-max-entries 10))) - (tags "open" ((org-agenda-overriding-header "\nUNDERTAKINGS:"))) - (todo "WAITING" ((org-agenda-overriding-header "\nWAITING:")))))) - org-agenda-start-with-log-mode t - org-agenda-log-mode-items '(closed clock state) - org-log-done 'time - org-log-into-drawer nil - org-log-state-notes-insert-after-drawers t - org-log-state-notes-into-drawer nil - org-capture-templates '(("i" "Inbox" entry - (file "gtd.org") - "* %^{Description} :inbox:\n%U\n%?" - :prepend t - :clock-in t - :clock-resume t) - ("m" "Meeting" entry - (file "gtd.org") - "* MEETING %^{Description} :meeting:\n%^{Scheduled:}T\n%?" - :prepend t - :clock-in t - :clock-resume t) - ("p" "Phone Call" entry - (file "gtd.org") - "* %^{Description} :call:\n%U\n%?" - :prepend t - :clock-in t - :clock-resume 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-auto-generate-ids-in-file () - "Add ID properties to all headlines in the current - file which do not already have one." - (interactive) - (org-map-entries 'org-id-get-create)) - -(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 -(defun re/org-archive-all () - "Archive all tasks marked `DONE' in agenda files" - (interactive) - (message "Archiving all completed tasks in org-agenda-files...") - (org-map-entries (lambda () - (org-archive-subtree) - (setq org-map-continue-from (org-element-property - :begin - (org-element-at-point)))) - "/DONE" - 'agenda) - (message "Archiving all completed tasks in org-agenda-files... done")) - -(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 -* HOWM -On-again, off-again relationship with this one -** Load package and add bindings -#+BEGIN_SRC emacs-lisp - (use-package howm - :bind - (("" . howm-menu) - ("S-" . re/howm-menu-new-frame) - :map howm-mode-map - ("C-c , k" . re/howm-create-keyword) - ("C-c , =" . howm-narrow-to-memo))) -#+END_SRC -** Config -*** General settings -#+BEGIN_SRC emacs-lisp -(setq howm-template '("= %title%cursor #inbox\n%date %file\n\n" - "= %title%cursor #meeting\n%date %file\n\nAttendees: \n" - "= @%title%cursor\n%date %file\n\n") - howm-keyword-file "~/howm/.howm-keys" - howm-history-file "~/howm/.howm-history" - howm-menu-file "~/.emacs.d/0000-00-00-000000.txt" - howm-file-name-format "%Y%m%dT%H%M%S.txt" - howm-keyword-case-fold-search t - howm-remember-insertion-format "%s" - howm-menu-todo-num 10) - -(defun re/howm-menu-new-frame (&optional arg) - "Show the howm menu in a new frame" - (interactive "P") - (re/open-with-new-frame "HOWM" #'howm-menu)) - -(defun re/howm-create-keyword () - "Reads a keyword from the minibuffer and appends it to the keywords file." - (interactive) - (append-to-file (concat (read-string "New Keyword: ") - "\n") - nil - howm-keyword-file - "\n")) -#+END_SRC -*** Menu config -#+BEGIN_SRC emacs-lisp -;; add entries in howm-menu (press enter on the text) -(defun re/howm-menu-legend () - (cl-labels ((p (text face) (propertize text 'font-lock-face face))) - (concat (format "Top %s" howm-menu-todo-num) - " " - (p "!Deadline" howm-reminder-deadline-face) - "/" - (p "+ToDo" howm-reminder-todo-face) - "/" - (p "@Schedule" howm-reminder-schedule-face) - "/" - (p "~Defer" howm-reminder-defer-face) - "/" - (p "-Reminder" howm-reminder-normal-face) - " Entries:"))) - -(with-eval-after-load 'howm - (dolist (aldr-additions '(("\\[Mtg\\]" (lambda (&optional dummy) (howm-create 2))) - ("\\[Person\\]" (lambda (&optional dummy) (howm-create 3))))) - (add-to-list 'action-lock-default-rules aldr-additions t)) - (add-to-list 'howm-menu-allow 're/howm-menu-legend) - ;; add coloring for custom "waiting" state - (add-to-list 'howm-reminder-font-lock-keywords - `(,(howm-reminder-regexp "[w]") (0 howm-reminder-defer-face prepend)) - t)) - -(dolist (hooks '(after-save-hook howm-mode-hook)) - (add-hook hooks 'howm-mode-set-buffer-name)) -#+END_SRC - -* Trashed -Shows the trash file as a dired buffer -#+BEGIN_SRC emacs-lisp -(use-package trashed - :config - (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 -* Editing -** UNIX/vi-like bindings -=C-h=, =C-w=, =C-u=, et. al to work as I expect. 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" "") ; Rather F1 for help, also C-S-H works - (keymap-global-set "M-j" (lambda () (interactive) (join-line 0))) ; join-line other way -#+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 "" 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 " " #'capitalize-dwim) ; M-c -(keymap-global-set " " #'downcase-dwim) ; M-l -(keymap-global-set " " #'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 "" (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)) - -(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 "" #'forward-page) -(keymap-global-set "" #'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 -(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 -* 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-set "" #'next-buffer) ; Forward button -;; Disable M-mouse for window manager bindings -(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/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 " " #'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 -(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)))) - -(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 -* Version-control mode -#+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))) - -(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) - -(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)))) - -(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))) - -(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) -#+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 -;; 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 -Rarely used, but nice to have when I want it. -#+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) - -(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 -;;; 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) -#+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 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) - -(dolist (hook '(org-mode-hook howm-mode-hook)) - (add-hook 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 -* Footer -#+BEGIN_SRC emacs-lisp -;;; config.el ends here -#+END_SRC diff --git a/config.org b/config.org new file mode 100644 index 0000000..3b1a4bc --- /dev/null +++ b/config.org @@ -0,0 +1,1362 @@ +#+TITLE: Emacs literate config +#+STARTUP: noindent +#+PROPERTY: header-args emacs-lisp :tangle yes + +* Header +#+BEGIN_SRC emacs-lisp +;;; config.el --- Emacs Configuration -*- lexical-binding: t; -*- + +;; This file is not part of GNU emacs + +;;; Code: + +#+END_SRC +* Load libraries +Libraries Not from the package manager +#+BEGIN_SRC emacs-lisp + (dolist (path '("sedit-mouse/" "themes/" "re/")) + (add-to-list 'load-path (concat user-emacs-directory path))) + + (dolist (file '(pass-alike eng-paper-theme sedit-mouse)) + (require file)) +#+END_SRC +* OS specific stuff +#+BEGIN_SRC emacs-lisp + (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 +* Add melpa, and reduce the declarations by setting defaults +#+BEGIN_SRC emacs-lisp +(require 'package) + +(add-to-list 'package-archives '("melpa" . "https://melpa.org/packages/") t) + +(setq use-package-always-ensure t + use-package-always-defer t) +#+END_SRC +* Corfu +#+BEGIN_SRC emacs-lisp +(use-package corfu + :demand + t + :custom + (corfu-auto t) + (corfu-auto-delay 0) + (corfu-auto-prefix 1) + :config + (global-corfu-mode)) + +(use-package corfu-popupinfo + :ensure + nil ; part of corfu + :after + corfu + :hook + (corfu-mode . corfu-popupinfo-mode) + :custom + (corfu-popupinfo-delay '(1.0 . 0.01)) ; start after one second, fast refresh once on + (corfu-popupinfo-hide nil) + :config + (corfu-popupinfo-mode)) +#+END_SRC +* Expand Region +#+BEGIN_SRC emacs-lisp +(use-package expand-region + :bind + (("C-=" . er/expand-region))) +#+END_SRC +* Aggressive Indent +#+BEGIN_SRC emacs-lisp +(use-package aggressive-indent) +#+END_SRC +* Magit +No longer needed, since I defer by default. +* Page Break Lines +Needs to be demanded so I don't have to manually load it +#+BEGIN_SRC emacs-lisp +(use-package page-break-lines + :demand + t + :config + (global-page-break-lines-mode)) +#+END_SRC +* AucTeX and pdftools +Make AucTeX use pdftools, prompt for master file, synctex, etc. Be sure to run =pdf-tools-install= before first use. +#+BEGIN_SRC emacs-lisp +(use-package pdf-tools) + +(use-package auctex + :config + (setq-default TeX-master nil) + (add-to-list 'TeX-view-program-selection '(output-pdf "PDF Tools")) + (setq TeX-auto-save t + TeX-parse-self t + TeX-PDF-mode t + 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 +(use-package csv-mode + :config + (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 +(use-package markdown-mode + :config + (setq markdown-special-ctrl-a/e t) + :bind + (:map markdown-mode-map + ("M-" . markdown-demote) + ("M-" . markdown-promote) + ("M-" . markdown-move-up) + ("M-" . markdown-move-down) + ("C-M-u" . markdown-up-heading) + ("C-M-b" . markdown-outline-previous-same-level) + ("C-M-f" . markdown-outline-next-same-level) + ("C-M-p" . markdown-outline-previous) + ("C-M-n" . markdown-outline-next))) + +(use-package edit-indirect :after 'markdown-mode) +#+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)) + + (use-package sly + :config + (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)))))) + :bind + (:map sly-mrepl-mode-map + (")" . (lambda () (interactive) (re/send-on-close-paren #'sly-mrepl-return))) + ("]" . (lambda () (interactive) (re/balance-and-eval-sexp #'sly-mrepl-return))) + ("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 + (use-package geiser + :config + (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) + :bind + (:map geiser-repl-mode-map + (")" . (lambda () (interactive) (re/send-on-close-paren #'geiser-repl-maybe-send))) + ("]" . (lambda () (interactive) (re/balance-and-eval-sexp #'geiser-repl-maybe-send))))) + + (use-package geiser-chicken :after 'geiser) +#+END_SRC +* Forth +** TODO Try out this mode for gforth +#+BEGIN_SRC emacs-lisp +(use-package forth-mode) +#+END_SRC +* ORG +** Load in the package and set bindings +#+BEGIN_SRC emacs-lisp + (use-package org + :config + (require 'org-mouse) + :bind + (("C-c l" . org-store-link) + ("C-c a" . org-agenda) + ("C-c c" . org-capture) + ("C-c b" . org-switchb) + ("" . org-clock-out) + ("" . org-clock-goto) + :map org-mode-map + ("M-" . org-shiftmetaright) + ("M-" . org-shiftmetaleft) + ("M-S-" . org-metaright) + ("M-S-" . org-metaleft) + ("C-M-h" . org-mark-subtree) + ("C-M-u" . org-up-element) + ("C-M-d" . org-down-element) + ("C-M-b" . org-backward-heading-same-level) + ("C-M-f" . org-forward-heading-same-level) + ("C-M-p" . org-previous-visible-heading) + ("C-M-n" . org-next-visible-heading) + ("" . org-clock-in))) + + (use-package org-transclusion + :after + org + :bind + (("C-c t" . org-transclusion-mode))) +#+END_SRC +** Configuration +*** General options +#+BEGIN_SRC emacs-lisp + (with-eval-after-load 'org-mode (add-to-list 'org-export-backends 'md)) + + (setq org-startup-indented nil + org-indent-indentation-per-level 1 + org-startup-folded 'fold + org-startup-with-inline-images t + org-cycle-inline-images-display t + org-cycle-separator-lines 0 + org-blank-before-new-entry '((heading) (plain-list-item . auto)) + org-M-RET-may-split-line nil + org-treat-S-cursor-todo-selection-as-state-change nil + org-treat-insert-todo-heading-as-state-change nil + org-table-export-default-format "orgtbl-to-csv" + org-list-allow-alphabetical t + org-fold-catch-invisible-edits 'show-and-error + org-ellipsis "↴" + org-tags-column 0 + org-reverse-note-order t + org-enforce-todo-dependencies t + org-enforce-todo-checkbox-dependencies t + org-fontify-done-headline nil + org-fontify-todo-headline nil + org-goto-interface 'outline-path-completion + org-outline-path-complete-in-steps nil + org-goto-auto-isearch nil + org-special-ctrl-a/e t + org-special-ctrl-k t + org-return-follows-link t + org-yank-adjusted-subtrees t + org-src-window-setup 'current-window + org-src-preserve-indentation t) +#+END_SRC +*** Time Clocking +#+BEGIN_SRC emacs-lisp +(org-clock-persistence-insinuate) +(setq org-clock-history-length 10 + 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 +#+BEGIN_SRC emacs-lisp +(add-hook 'org-agenda-mode-hook #'hl-line-mode) + +(setq org-agenda-files (append '("~/org/") + (file-expand-wildcards "~/projects/*/")) + 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 '(("call" . ?c) + ("email" . ?e) + ("errand" . ?r) + ("jobpc" . ?j) + ("license" . ?l) + ("meeting" . ?m) + ("open" . ?o)) + org-tags-exclude-from-inheritance '("open") + 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) + ("MEETING" :foreground "#315e9a" :weight bold)) + org-agenda-restore-windows-after-quit t + org-agenda-start-on-weekday nil + org-agenda-compact-blocks t + org-agenda-span 'day + org-deadline-warning-days 30 + org-agenda-window-setup 'current-window + org-agenda-time-grid '((daily today remove-match) + (600 700 800 900 1000 1100 1200 1300 1400 1500 1600 1700 1800 1900) + " ――――― " + "") + org-agenda-tags-column 50 + org-agenda-skip-additional-timestamps-same-entry t + org-agenda-skip-deadline-if-done t + org-agenda-skip-scheduled-if-done t + org-agenda-prefix-format '((agenda . " %?-12t% s") + (todo . " ") + (tags . " ") + (search . " ")) + org-agenda-scheduled-leaders '("BOOKED: " "Booked %2d d. ago: ") + org-agenda-deadline-leaders '("DEADLINE: " "In %3d d.: " "%2d d. ago: ") + org-read-date-prefer-future 'time + org-agenda-custom-commands '(("f" "Maybe Someday" tags "someday" ((org-agenda-overriding-header "SOMEDAY:"))) + ("i" "Inbox" tags "inbox" ((org-agenda-overriding-header "INBOX:"))) + ("l" "Licenses" tags "license" ((org-agenda-overriding-header "LICENSES:"))) + ("n" "Next Actions" tags-todo "-recurring/TODO" ((org-agenda-overriding-header "NEXT:"))) + ("p" "JobPCs" tags "jobpc" ((org-agenda-overriding-header "JOB PCS:"))) + ("u" "Undertakings" tags "open" ((org-agenda-overriding-header "UNDERTAKINGS:"))) + ("w" "Waiting" todo "WAITING" ((org-agenda-overriding-header "WAITING:"))) + ("o" "Overview" ((agenda "") + (tags "inbox" ((org-agenda-overriding-header "\nINBOX:"))) + (tags-todo "-recurring/TODO" ((org-agenda-overriding-header "\nNEXT:") + (org-agenda-max-entries 10))) + (tags "open" ((org-agenda-overriding-header "\nUNDERTAKINGS:"))) + (todo "WAITING" ((org-agenda-overriding-header "\nWAITING:")))))) + org-agenda-start-with-log-mode t + org-agenda-log-mode-items '(closed clock state) + org-log-done 'time + org-log-into-drawer nil + org-log-state-notes-insert-after-drawers t + org-log-state-notes-into-drawer nil + org-capture-templates '(("i" "Inbox" entry + (file "gtd.org") + "* %^{Description} :inbox:\n%U\n%?" + :prepend t + :clock-in t + :clock-resume t) + ("m" "Meeting" entry + (file "gtd.org") + "* MEETING %^{Description} :meeting:\n%^{Scheduled:}T\n%?" + :prepend t + :clock-in t + :clock-resume t) + ("p" "Phone Call" entry + (file "gtd.org") + "* %^{Description} :call:\n%U\n%?" + :prepend t + :clock-in t + :clock-resume 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-auto-generate-ids-in-file () + "Add ID properties to all headlines in the current + file which do not already have one." + (interactive) + (org-map-entries 'org-id-get-create)) + +(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 +(defun re/org-archive-all () + "Archive all tasks marked `DONE' in agenda files" + (interactive) + (message "Archiving all completed tasks in org-agenda-files...") + (org-map-entries (lambda () + (org-archive-subtree) + (setq org-map-continue-from (org-element-property + :begin + (org-element-at-point)))) + "/DONE" + 'agenda) + (message "Archiving all completed tasks in org-agenda-files... done")) + +(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 +* HOWM +On-again, off-again relationship with this one +** Load package and add bindings +#+BEGIN_SRC emacs-lisp + (use-package howm + :bind + (("" . howm-menu) + ("S-" . re/howm-menu-new-frame) + :map howm-mode-map + ("C-c , k" . re/howm-create-keyword) + ("C-c , =" . howm-narrow-to-memo))) +#+END_SRC +** Config +*** General settings +#+BEGIN_SRC emacs-lisp +(setq howm-template '("= %title%cursor #inbox\n%date %file\n\n" + "= %title%cursor #meeting\n%date %file\n\nAttendees: \n" + "= @%title%cursor\n%date %file\n\n") + howm-keyword-file "~/howm/.howm-keys" + howm-history-file "~/howm/.howm-history" + howm-menu-file "~/.emacs.d/0000-00-00-000000.txt" + howm-file-name-format "%Y%m%dT%H%M%S.txt" + howm-keyword-case-fold-search t + howm-remember-insertion-format "%s" + howm-menu-todo-num 10) + +(defun re/howm-menu-new-frame (&optional arg) + "Show the howm menu in a new frame" + (interactive "P") + (re/open-with-new-frame "HOWM" #'howm-menu)) + +(defun re/howm-create-keyword () + "Reads a keyword from the minibuffer and appends it to the keywords file." + (interactive) + (append-to-file (concat (read-string "New Keyword: ") + "\n") + nil + howm-keyword-file + "\n")) +#+END_SRC +*** Menu config +#+BEGIN_SRC emacs-lisp +;; add entries in howm-menu (press enter on the text) +(defun re/howm-menu-legend () + (cl-labels ((p (text face) (propertize text 'font-lock-face face))) + (concat (format "Top %s" howm-menu-todo-num) + " " + (p "!Deadline" howm-reminder-deadline-face) + "/" + (p "+ToDo" howm-reminder-todo-face) + "/" + (p "@Schedule" howm-reminder-schedule-face) + "/" + (p "~Defer" howm-reminder-defer-face) + "/" + (p "-Reminder" howm-reminder-normal-face) + " Entries:"))) + +(with-eval-after-load 'howm + (dolist (aldr-additions '(("\\[Mtg\\]" (lambda (&optional dummy) (howm-create 2))) + ("\\[Person\\]" (lambda (&optional dummy) (howm-create 3))))) + (add-to-list 'action-lock-default-rules aldr-additions t)) + (add-to-list 'howm-menu-allow 're/howm-menu-legend) + ;; add coloring for custom "waiting" state + (add-to-list 'howm-reminder-font-lock-keywords + `(,(howm-reminder-regexp "[w]") (0 howm-reminder-defer-face prepend)) + t)) + +(dolist (hooks '(after-save-hook howm-mode-hook)) + (add-hook hooks 'howm-mode-set-buffer-name)) +#+END_SRC + +* Trashed +Shows the trash file as a dired buffer +#+BEGIN_SRC emacs-lisp +(use-package trashed + :config + (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 +* Editing +** UNIX/vi-like bindings +=C-h=, =C-w=, =C-u=, et. al to work as I expect. 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" "") ; Rather F1 for help, also C-S-H works + (keymap-global-set "M-j" (lambda () (interactive) (join-line 0))) ; join-line other way +#+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 "" 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 " " #'capitalize-dwim) ; M-c +(keymap-global-set " " #'downcase-dwim) ; M-l +(keymap-global-set " " #'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 "" (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)) + +(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 "" #'forward-page) +(keymap-global-set "" #'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 +(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 +* 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-set "" #'next-buffer) ; Forward button +;; Disable M-mouse for window manager bindings +(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/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 " " #'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 +(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)))) + +(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 +* Version-control mode +#+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))) + +(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) + +(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)))) + +(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))) + +(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) +#+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 +;; 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 +Rarely used, but nice to have when I want it. +#+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) + +(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 +;;; 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) +#+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 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) + +(dolist (hook '(org-mode-hook howm-mode-hook)) + (add-hook 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 +* Footer +#+BEGIN_SRC emacs-lisp +;;; config.el ends here +#+END_SRC diff --git a/init.el b/init.el index 237b528..0b4d640 100644 --- a/init.el +++ b/init.el @@ -4,11 +4,6 @@ ;;; Code: -(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)) +(org-babel-load-file "~/.emacs.d/config.org") ;;; init.el ends here