summaryrefslogtreecommitdiff
path: root/config/emacs/site-lisp/my-defines.el
diff options
context:
space:
mode:
authorThomas Ulmer <thomasmulmer02@gmail.com>2026-03-22 20:00:38 -0700
committerThomas Ulmer <thomas.ulmer@redflagai.co>2026-03-23 13:57:26 -0700
commit3a5cdbdb8f1fdd777f0fd8aa69aeaa55379fea64 (patch)
treefa2055ceec4053bf4a0cd99701d3deccde7267df /config/emacs/site-lisp/my-defines.el
parentfeb68222737976be632dcbf0ea4dc29656201c4c (diff)
emacs move to separate files
Diffstat (limited to 'config/emacs/site-lisp/my-defines.el')
-rw-r--r--config/emacs/site-lisp/my-defines.el363
1 files changed, 363 insertions, 0 deletions
diff --git a/config/emacs/site-lisp/my-defines.el b/config/emacs/site-lisp/my-defines.el
new file mode 100644
index 0000000..425a23e
--- /dev/null
+++ b/config/emacs/site-lisp/my-defines.el
@@ -0,0 +1,363 @@
+;;; Custom definitions, with prefix "my/" -*- lexical-binding: t; -*-
+
+(defvar surround-keys '("(" "{" "[" "$" "\"" "'"))
+(defun my/wrap-pair ()
+ "Ensure the region is active and then \"insert-pair\"."
+ (interactive)
+ (activate-mark)
+ (insert-pair))
+(eval
+ `(define-keymap
+ :full nil
+ :name "Surround"
+ :prefix 'surround-prefix
+ ,@(mapcan
+ (lambda (key) (list key ''my/wrap-pair))
+ surround-keys
+ )))
+
+(defun my/toggle-frame-transparency ()
+ "Toggle transparency."
+ (interactive)
+ (let ((alpha-transparency 90))
+ (if (eq alpha-transparency (frame-parameter nil 'alpha-background))
+ (set-frame-parameter nil 'alpha-background 100)
+ (set-frame-parameter nil 'alpha-background alpha-transparency))))
+
+(defvar my/prog-cleanup-excluded-modes
+ '(makefile-gmake-mode makefile-mode makefile-imake-mode
+ makefile-makepp-mode makefile-bsdmake-mode
+ makefile-automake-mode
+ python-mode
+ fundamental-mode)
+ "Major modes for which \"my/clean-prog-file\" should not be run before save.")
+
+(defvar my/prog-cleanup-want-tabs-modes
+ '(makefile-gmake-mode makefile-mode makefile-imake-mode
+ makefile-makepp-mode makefile-bsdmake-mode makefile-automake-mode)
+ "Major modes that need tabs instead of spaces.
+\"my/clean-prog-file\" should convert spaces to tabs instead of tabs to spaces.")
+
+(defvar my/prog-cleanup-indent-max-lines 500
+ "Skip reindenting the buffer if it is longer than this many lines.
+Nil for no limit.")
+
+(define-minor-mode my/global-prog-clean-mode
+ "Do auto formating on save in prog mode buffers."
+ :global t
+ (if my/global-prog-clean-mode
+ (progn ;enable
+ (add-hook 'before-save-hook 'my/clean-prog-file))
+ (progn ;disable
+ (remove-hook 'before-save-hook 'my/clean-prog-file))))
+(my/global-prog-clean-mode 1)
+
+(defun my/clean-prog-file (&optional buffer)
+ "Perform whitespace cleanup and some normalization on `buffer'.
+Defaults to the current BUFFER. Converts tabs to spaces unless the
+major mode is in `my/prog-cleanup-want-tabs-modes', otherwise do the
+reverse. Delete trailing whitespace."
+ (unless buffer (setq buffer (current-buffer)))
+ (with-current-buffer buffer
+ (when (and my/global-prog-clean-mode
+ (not (member major-mode my/prog-cleanup-excluded-modes))
+ (derived-mode-p '(prog-mode))
+ (cond
+ ((and (functionp 'clang-format-buffer)
+ ; safety valve for undef'd
+ (derived-mode-p '(c-mode c++-mode)))
+ (clang-format-buffer))
+ (t
+ (when (and my/prog-cleanup-indent-max-lines
+ (< (car (buffer-line-statistics buffer))
+ my/prog-cleanup-indent-max-lines))
+ (indent-region (point-min) (point-max)))))
+ (if (member major-mode my/prog-cleanup-want-tabs-modes)
+ (tabify (point-min) (point-max))
+ (untabify (point-min) (point-max)))
+ (delete-trailing-whitespace)))))
+
+(defun my/prog-buffer-setup ()
+ "Do setup in for code buffers."
+ ;; (superword-mode 1)
+ ;; (modify-syntax-entry ?_ "w")
+ ;; (hs-minor-mode 1)
+ (display-fill-column-indicator-mode 1)
+ (smerge-mode 1)
+ (ignore-errors
+ (when (and (derived-mode-p '(c-mode c++-mode))
+ (> (car (buffer-line-statistics)) 0))
+ (c-guess))))
+(add-hook 'prog-mode-hook 'my/prog-buffer-setup)
+
+(defun my/wrap-region-paren ()
+ "Wraps the region in parens. Similar to pressing ( with the region active."
+ (interactive)
+ (let ((beg (region-beginning))
+ (end (region-end)))
+ (save-excursion
+ (goto-char beg)
+ (insert "(")
+ (goto-char (+ end 1))
+ (insert ")"))))
+
+(defun my/zap-whitespace ()
+ "Zaps (deletes) the whitespace around the point in both directions."
+ (interactive)
+ (push-mark)
+ (skip-chars-backward " \t\n")
+ (push-mark)
+ (skip-chars-forward " \t\n")
+ (delete-region (mark) (point))
+ (pop-mark)
+ (goto-char (mark))
+ (pop-mark))
+
+(defun my/wrap-sexp (&optional arg)
+ "Wraps the following sexp in a pair of parens.
+Places point inside the new pair, pushing the mark before the new pair.
+With prefix ARG do ARG number sexps if given, including backwards."
+ (interactive "p")
+ (message "%s" arg)
+ (if arg
+ (progn
+ (push-mark)
+ (insert-pair arg ?\( ?\)))
+ (insert-pair 1 ?\( ?\))))
+
+(defun my/toggle-window-other-reachability ()
+ "Toggle whether this window can be selected by `other-window'."
+ (interactive)
+ (let* ((win (get-buffer-window (current-buffer)))
+ (current (window-parameter win 'no-other-window)))
+ (set-window-parameter win 'no-other-window (not current))
+ (force-mode-line-update)))
+;; small hack to make other window reachability visible like window dedication
+(advice-add 'mode-line-window-control :around
+ (lambda (f) (concat (funcall f)
+ (if (window-parameter nil 'no-other-window) "o" "")))
+ '(:name "other-window-indicator"))
+
+;; TODO find a bind for this
+(defun my/open-window-by-buffer (buffer)
+ "Move focus to an extant window by the buffer it contains.
+If no such window exists, switch to BUFFER in the current window."
+ (interactive "bBuffer: ")
+ (let ((win (get-buffer-window buffer t)))
+ (if win
+ (select-window win)
+ (switch-to-buffer buffer))))
+
+(defun my/shorten-path (path)
+ "Shorten string PATH. Initialize all but the last path component."
+ (let* ((components (split-string path "/"))
+ (last-component (-last (lambda (s) (not (string-empty-p s))) components))
+ (shortened-components
+ (mapcar (lambda (comp)
+ (if (or (string-equal comp last-component) (string-empty-p comp))
+ comp
+ (substring comp 0 1)))
+ components)))
+ (mapconcat 'identity shortened-components "/")))
+
+(defun my/swap-other-window-buffer ()
+ "Exchange buffer contents with the next window.
+Tries to switch the buffer contents of the current window and the next
+in the cyclic ordering. Doesn't consider windows that are dedicated."
+ (interactive)
+ (let* ((cw (get-mru-window nil nil nil t))
+ (cb (window-buffer cw))
+ (ow (next-window))
+ (ob (window-buffer ow)))
+ (unless (window-dedicated-p cw)
+ (while (window-dedicated-p ow)
+ (setq ow (next-window ow)
+ ob (window-buffer ow)))
+ (unless (eq cw ow)
+ (progn
+ (with-selected-window ow
+ (switch-to-buffer cb))
+ (with-selected-window cw
+ (switch-to-buffer ob)))))))
+
+(defun my/activate-mark ()
+ "Activate the region a la `C-SPC C-SPC', but don't move the point.
+Useful in combination with `query-replace' for acting on the region."
+ (interactive)
+ (if (region-active-p)
+ (deactivate-mark)
+ (activate-mark)))
+
+(defun my/c-x-n-to-m-n (&optional keymap)
+ "Copy bindings from C-x [n] to M-[n] in KEYMAP, defaulting to the global
+keymap."
+ (unless keymap (setq keymap global-map))
+ (mapc (lambda (n)
+ (let* ((from (concat "C-x " (format "%d" n)))
+ (to (concat "M-" (format "%d" n)))
+ (bind (keymap-lookup keymap from)))
+ (keymap-set keymap to bind)))
+ '(0 1 2 3 4 5 6 7 8 9)))
+(my/c-x-n-to-m-n)
+
+(defun my/keymap-keys (map)
+ "Return a list of all key sequences in a keymap MAP recursively."
+ (mapcan (lambda (c)
+ (let ((ks (car c))
+ (km (cdr c))
+ (acc nil))
+ (map-keymap (lambda (key bind)
+ (let ((kss (vconcat ks (vector key))))
+ (when (commandp bind)
+ (push kss acc))))
+ km)
+ acc))
+ (accessible-keymaps map)))
+
+(defun my/eshell (&optional arg)
+ "Create a new eshell or switch to an existing one.
+With ARG, make a new shell. If the current buffer is an eshell, switch
+back to the previous selected buffer."
+ (interactive "P")
+ (if (eq major-mode 'eshell-mode)
+ (switch-to-prev-buffer)
+ (eshell arg)))
+
+(defun my/eshell-clear-or-center (&optional clear)
+ "Either clear the screen or recenter display depending on point location."
+ (interactive "P")
+ (cond
+ (clear (eshell/clear clear))
+ ((>= (point) eshell-last-output-end) ;; at the input line
+ (eshell/clear clear))
+ (t ;; in scroll back somewhere
+ (recenter-top-bottom))))
+
+(defun my/vc-on-root (&optional arg)
+ "Open a vc dir buffer at the project root.
+With ARG, open a Dired buffer instead."
+ (interactive "P")
+ (let ((dir (project-root (project-current t))))
+ (if arg
+ (dired dir)
+ (vc-dir dir))))
+
+(defun my/shell-at-root ()
+ "Execute an async shell commmand at the root of the current project."
+ (interactive)
+ (let ((prior-cwd default-directory)
+ (want-quit inhibit-quit))
+ (setq inhibit-quit t)
+ (with-local-quit
+ (setq default-directory (project-root (project-current)))
+ (call-interactively 'async-shell-command))
+ (setq default-directory prior-cwd
+ inhibit-quit want-quit)))
+
+(defun my/kill-buffer (&optional arg)
+ "Kill the current buffer. With ARG kill the window as well."
+ (interactive "P")
+ (if arg
+ (kill-buffer-and-window)
+ (kill-buffer)))
+
+(defvar my/layout-pair (cons nil nil)
+ "Cons pair of window configurations.")
+(add-hook 'after-init-hook
+ (lambda ()
+ (setq my/layout-pair (cons (current-window-configuration)
+ (current-window-configuration)))))
+(defun my/layout-swap (&optional arg)
+ "Swap between the two saved window configurations.
+With ARG, don't save the current layout."
+ (interactive "p")
+ (let ((left (car my/layout-pair))
+ (right (cdr my/layout-pair))
+ (cwc (current-window-configuration)))
+ (cond
+ ((>= arg 16) (setq my/layout-pair (cons cwc cwc)))
+ ((eq (selected-frame) (window-configuration-frame right))
+ (unless (>= arg 4) (setq left cwc))
+ (set-window-configuration right t t)
+ (setq my/layout-pair (cons right left)))
+ (t (message "Quick layout swap only works with same frame!")))))
+
+(defun my/display-lines-advice (func &rest r)
+ "Around advice that temporarily shows line numbers."
+ (let ((want-quit inhibit-quit))
+ (setq inhibit-quit t)
+ (display-line-numbers-mode 1)
+ (force-window-update)
+ (with-local-quit
+ (call-interactively func))
+ (display-line-numbers-mode -1)
+ (force-window-update)
+ (setq inhibit-quit want-quit)))
+
+(progn ; my global keybinds
+ (define-minor-mode my/keys-mode
+ "Minor mode collecting global keybinds"
+ :global t
+ :keymap (make-sparse-keymap)
+
+ (if my/keys-mode
+ (repeat-mode 1)
+ (repeat-mode -1)))
+ (require 'eshell)
+ (require 'em-prompt)
+ (put 'other-window 'repeat-map nil)
+ (put 'other-window-backward 'repeat-map nil)
+ (put 'eshell-previous-prompt 'repeat-map nil)
+ (put 'eshell-next-prompt 'repeat-map nil)
+
+ ;; The keymaps in `emulation-mode-map-alists' take precedence over
+ ;; `minor-mode-map-alist'
+ (add-to-list 'emulation-mode-map-alists
+ '((my/keys-mode . my/keys-keymap)))
+
+ (keymap-set my/keys-mode-map "C-x -" 'my/swap-other-window-buffer)
+ (keymap-set my/keys-mode-map "C-x w o" 'my/toggle-window-other-reachability)
+ (keymap-set my/keys-mode-map "C-x w r r" 'window-layout-rotate-clockwise)
+ (keymap-set my/keys-mode-map "C-x w r R" 'window-layout-rotate-anticlockwise)
+ (keymap-set window-layout-rotate-repeat-map "r" 'window-layout-rotate-clockwise)
+ (keymap-set window-layout-rotate-repeat-map "R" 'window-layout-rotate-anticlockwise)
+
+ (keymap-set my/keys-mode-map "C-M-z" 'my/wrap-sexp)
+
+ (keymap-set my/keys-mode-map "C-c R" 'raise-sexp)
+ (keymap-set my/keys-mode-map "C-c r" 'delete-pair)
+
+ (keymap-set my/keys-mode-map "M-s g" 'grep)
+ (keymap-set my/keys-mode-map "M-s r" 'query-replace-regexp)
+ (keymap-set my/keys-mode-map "M-s a" 'align-regexp)
+
+ (keymap-set my/keys-mode-map "M-w" 'copy-region-as-kill)
+ (keymap-set my/keys-mode-map "M-(" 'my/wrap-region-paren)
+ (keymap-set my/keys-mode-map "M-z" 'my/zap-whitespace)
+ (keymap-set my/keys-mode-map "M-o" 'other-window)
+ (keymap-set my/keys-mode-map "M-O" 'other-window-backward)
+ (keymap-set my/keys-mode-map "M-RET" 'my/eshell)
+ (keymap-set my/keys-mode-map "M-C" 'calc)
+ (keymap-set my/keys-mode-map "M-K" 'my/kill-buffer)
+ (keymap-set my/keys-mode-map "M-D" 'my/vc-on-root)
+ (keymap-set my/keys-mode-map "M-Q" 'auto-fill-mode)
+ (keymap-set my/keys-mode-map "M-A" 'beginning-of-line-text)
+ (keymap-set my/keys-mode-map "M-R" 'my/layout-swap)
+ (keymap-set my/keys-mode-map "M-W" 'switch-to-buffer)
+ (keymap-set my/keys-mode-map "M-N" 'man)
+ (keymap-set my/keys-mode-map "M-*" 'my/shell-at-root)
+ (keymap-set my/keys-mode-map "M-S" 'surround-prefix)
+ (keymap-set my/keys-mode-map "M-J" 'my/activate-mark)
+
+ (my/keys-mode 1))
+
+(progn ; mode for log files
+ (define-derived-mode
+ generic-log-mode
+ fundamental-mode
+ "Log"
+ "Major mode for following log files."
+ (setq-local auto-revert-verbose nil)
+ (auto-revert-tail-mode 1))
+ (add-to-list 'auto-mode-alist
+ '("\\.log\\'" . generic-log-mode)))