Files
lypacamacs/custom/utils.el
T
2026-07-30 20:21:27 +02:00

123 lines
4.7 KiB
EmacsLisp

;;; 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)