;;; utils.el --- General utility functions -*- lexical-binding: t; -*- (defun my/delete-word-backward () "Delete the previous 'block' of characters without killing them. Blocks are defined by: whitespace, alphanumerics, or groups of symbols." (interactive) (if (use-region-p) (delete-region (region-beginning) (region-end)) (let ((end (point))) (save-excursion (cond ;; Case 1: Multiple spaces or tabs (but not a newline) ((looking-back "[ \t]+" 1) (re-search-backward "[^ \t]" nil t) (forward-char 1)) ;; Case 2: Newlines ((looking-back "\n+" 1) (re-search-backward "[^\n]" nil t) (forward-char 1)) ;; Case 3: Alphanumerics (Words) ((looking-back "[[:alnum:]]+" 1) (re-search-backward "[^[:alnum:]]" nil t) (forward-char 1)) ;; Case 4: Special Characters (Symbols) (t (re-search-backward "[[:alnum:][:space:]]" nil t) (forward-char 1))) (delete-region (point) end))))) (defun dired-follow-symlink () "In dired, visit the file or directory at point by its true physical path." (interactive) (let ((filename (dired-get-file-for-visit))) (if filename (find-file (file-truename filename)) (message "No file at point")))) (defun my/column-align-regexp () "Aligns the matched regexp to `comment-column`. If the text preceding the regexp is already past `comment-column`, it ensures exactly one space of separation." (interactive) (let* ((regexp (read-string "Align on regexp: ")) ;; Use comment-column if set, otherwise default to 40 (target-col (if (and (boundp 'comment-column) (numberp comment-column)) comment-column 80)) (start (region-beginning)) (end (region-end))) (save-excursion (save-restriction (narrow-to-region start end) (goto-char (point-min)) (while (re-search-forward regexp nil t) (let* ((match-start (match-beginning 0)) (match-end (match-end 0))) ;; 1. Find the end of the actual content before the match ;; We move back from the match start and skip all whitespace (save-excursion (goto-char match-start) (skip-chars-backward " \t") (let ((content-end-pos (point)) (content-end-col (current-column))) ;; Delete the whitespace between the content and the match (delete-region content-end-pos match-start) ;; Calculate how much space we need (if (< content-end-col target-col) (let ((num-spaces (- target-col content-end-col))) (insert (make-string num-spaces ?\s))) ;; If content is already past the target, just insert one space (insert " ")))) ;; Move point to the end of the match to continue searching (goto-char match-end))))))) ;; Optional: Bind it to a key for easier access ;; (global-set-key (kbd "C-c a l") 'my/column-align-regexp) (defun my/list-no-prefix-bindings (keymap &optional map-name) "Extract and display all single-key (no-prefix) bindings from KEYMAP. Outputs a clean list into a dedicated buffer." (interactive (list global-map "global-map")) (let ((buf (get-buffer-create "*No-Prefix Bindings*")) (bindings '())) (cl-labels ((map-extract (map) (map-keymap (lambda (event binding) (cond ;; Skip nested/prefix keymaps entirely ((keymapp binding) nil) ;; Handle standard events (symbols or characters) ((or (symbolp event) (integerp event)) (let ((key-str (single-key-description event))) ;; Filter for simple keys (no spaces, meaning no multi-key sequences) (unless (string-match-p " " key-str) (push (cons key-str binding) bindings)))))) map))) (map-extract keymap) ;; Sort logically by key description ;; Print the results cleanly (with-current-buffer buf (text-mode) (setq buffer-read-only nil) (erase-buffer) (insert (format "No-Prefix Bindings for: %s\n" (or map-name "Selected Map"))) (insert "==================================================\n\n") (dolist (b bindings) (insert (format "%-15s -> %s\n" (car b) (cdr b)))) (setq buffer-read-only t)) (pop-to-buffer buf)))) (provide 'utils)