diff options
| author | Craig Jennings <c@cjennings.net> | 2026-06-29 04:41:40 -0400 |
|---|---|---|
| committer | Craig Jennings <c@cjennings.net> | 2026-06-29 04:41:40 -0400 |
| commit | ca3d447bfd849b54f8f9d74568f3dee1e86c391e (patch) | |
| tree | 86a1f7d79310cf57ed23b2f5b02be7675badc25e /modules | |
| parent | a56a714ce30fe91bd5afd0ba181d0d4bc508a8c0 (diff) | |
| download | dotemacs-ca3d447bfd849b54f8f9d74568f3dee1e86c391e.tar.gz dotemacs-ca3d447bfd849b54f8f9d74568f3dee1e86c391e.zip | |
refactor: split custom-misc.el into focused modules
custom-misc.el was an incoherent grab-bag, so anything small defaulted to landing there. I split its eight commands by concern. Three moved into new modules: custom-format (region/buffer reformat), custom-counts (word and character counts), and custom-text-transform (fraction glyphs). The other three went to existing homes: the previous-buffer toggle to custom-buffer-file, the delimiter jump to custom-line-paragraph, and the align-regexp space advice with its alignment and fill bindings to custom-whitespace.
The C-; bindings, which-key labels, and the six test files moved with their functions, and custom-misc.el is deleted. No behavior change: every command keeps its name and its C-; key.
Diffstat (limited to 'modules')
| -rw-r--r-- | modules/custom-buffer-file.el | 9 | ||||
| -rw-r--r-- | modules/custom-counts.el | 63 | ||||
| -rw-r--r-- | modules/custom-format.el | 46 | ||||
| -rw-r--r-- | modules/custom-line-paragraph.el | 31 | ||||
| -rw-r--r-- | modules/custom-misc.el | 196 | ||||
| -rw-r--r-- | modules/custom-text-transform.el | 63 | ||||
| -rw-r--r-- | modules/custom-whitespace.el | 16 |
7 files changed, 228 insertions, 196 deletions
diff --git a/modules/custom-buffer-file.el b/modules/custom-buffer-file.el index b10ecd168..261956f37 100644 --- a/modules/custom-buffer-file.el +++ b/modules/custom-buffer-file.el @@ -574,5 +574,14 @@ Signals an error if: "C-; b <down>" "resize divider down")) +;; --- previous-buffer toggle (formerly in custom-misc.el) --- +(defun cj/switch-to-previous-buffer () + "Switch to previously open buffer. +Repeated invocations toggle between the two most recently open buffers." + (interactive) + (switch-to-buffer (other-buffer (current-buffer) 1))) + +(cj/register-command "SPC" #'cj/switch-to-previous-buffer "prev buffer") + (provide 'custom-buffer-file) ;;; custom-buffer-file.el ends here. diff --git a/modules/custom-counts.el b/modules/custom-counts.el new file mode 100644 index 000000000..792732a40 --- /dev/null +++ b/modules/custom-counts.el @@ -0,0 +1,63 @@ +;;; custom-counts.el --- Word and character counts -*- coding: utf-8; lexical-binding: t; -*- + +;;; Commentary: +;; +;; Layer: 2 (Core UX). +;; Category: L. +;; Load shape: eager. +;; Eager reason: registers its C-; # w and C-; # c command bindings at load. +;; Top-level side effects: binds the count commands under C-; # w and C-; # c. +;; Runtime requires: keybindings. +;; Direct test load: yes (requires keybindings explicitly). +;; +;; Count words or characters in the active region, or the whole buffer when no +;; region is active, and report the total in the minibuffer. Split out of the +;; former custom-misc.el grab-bag. + +;;; Code: + +(require 'keybindings) ;; provides cj/register-command + +(defun cj/--count-words (start end) + "Internal implementation: Count words between START and END. +START and END define the region to count. +Returns the word count as an integer." + (when (> start end) + (error "Invalid region: start (%d) is greater than end (%d)" start end)) + (count-words start end)) + +(defun cj/count-words-buffer-or-region () + "Count the number of words in the buffer or region. +Display the result in the minibuffer." + (interactive) + (let* ((use-region (use-region-p)) + (begin (if use-region (region-beginning) (point-min))) + (end (if use-region (region-end) (point-max))) + (area-type (if use-region "the region" "the buffer")) + (word-count (cj/--count-words begin end))) + (message "There are %d words in %s." word-count area-type))) + +(defun cj/--count-characters (start end) + "Internal implementation: Count characters between START and END. +START and END define the region to count. +Returns the character count as an integer." + (when (> start end) + (error "Invalid region: start (%d) is greater than end (%d)" start end)) + (- end start)) + +(defun cj/count-characters-buffer-or-region () + "Count the number of characters in the buffer or region. +Display the result in the minibuffer." + (interactive) + (let* ((use-region (use-region-p)) + (begin (if use-region (region-beginning) (point-min))) + (end (if use-region (region-end) (point-max))) + (area-type (if use-region "the region" "the buffer")) + (char-count (cj/--count-characters begin end))) + (message "There are %d characters in %s." char-count area-type))) + +(cj/register-command "# w" #'cj/count-words-buffer-or-region "count words") +(cj/register-command "# c" #'cj/count-characters-buffer-or-region "count characters") + +(provide 'custom-counts) +;;; custom-counts.el ends here diff --git a/modules/custom-format.el b/modules/custom-format.el new file mode 100644 index 000000000..47cd7d88d --- /dev/null +++ b/modules/custom-format.el @@ -0,0 +1,46 @@ +;;; custom-format.el --- Region and buffer reformatting -*- coding: utf-8; lexical-binding: t; -*- + +;;; Commentary: +;; +;; Layer: 2 (Core UX). +;; Category: L. +;; Load shape: eager. +;; Eager reason: registers its C-; f command binding at load. +;; Top-level side effects: binds cj/format-region-or-buffer under C-; f. +;; Runtime requires: keybindings. +;; Direct test load: yes (requires keybindings explicitly). +;; +;; Reformat the active region, or the whole buffer when no region is active: +;; untabify, reindent, and delete trailing whitespace. Split out of the +;; former custom-misc.el grab-bag. + +;;; Code: + +(require 'keybindings) ;; provides cj/register-command + +(defun cj/--format-region (start end) + "Internal implementation: Reformat text between START and END. +START and END define the region to operate on. +Replaces tabs with spaces, reindents, and deletes trailing whitespace." + (when (> start end) + (error "Invalid region: start (%d) is greater than end (%d)" start end)) + (save-excursion + (save-restriction + (narrow-to-region start end) + (untabify (point-min) (point-max)) + (indent-region (point-min) (point-max)) + (delete-trailing-whitespace (point-min) (point-max))))) + +(defun cj/format-region-or-buffer () + "Reformat the region or the entire buffer. +Replaces tabs with spaces, deletes trailing whitespace, and reindents." + (interactive) + (let ((start-pos (if (use-region-p) (region-beginning) (point-min))) + (end-pos (if (use-region-p) (region-end) (point-max)))) + (cj/--format-region start-pos end-pos) + (message "Formatted %s" (if (use-region-p) "region" "buffer")))) + +(cj/register-command "f" #'cj/format-region-or-buffer "format buffer") + +(provide 'custom-format) +;;; custom-format.el ends here diff --git a/modules/custom-line-paragraph.el b/modules/custom-line-paragraph.el index dd2999c4e..d29d4125b 100644 --- a/modules/custom-line-paragraph.el +++ b/modules/custom-line-paragraph.el @@ -166,5 +166,36 @@ If the line is empty or contains only whitespace, abort with a message." "C-; l r" "remove matching" "C-; l u" "underscore line")) +;; --- delimiter jump (formerly in custom-misc.el) --- +(defun cj/jump-to-matching-paren () + "Jump to the matching delimiter if point is on (or just after) one. +If not on a delimiter, show a message. Respects the current syntax table." + (interactive) + (let* ((ca (char-after)) + (cb (char-before)) + ;; Check if on opening paren + (open-p (and ca (eq (char-syntax ca) ?\())) + ;; Check if on or just after closing paren + (close-p (or (and ca (eq (char-syntax ca) ?\))) + (and cb (eq (char-syntax cb) ?\)))))) + (cond + ;; Jump forward from opening + (open-p + (condition-case err + (forward-sexp) + (scan-error + (message "No matching delimiter: %s" (error-message-string err))))) + ;; Jump backward from closing + (close-p + (condition-case err + (backward-sexp) + (scan-error + (message "No matching delimiter: %s" (error-message-string err))))) + ;; Not on delimiter + (t + (message "Point is not on a delimiter."))))) + +(cj/register-command ")" #'cj/jump-to-matching-paren "jump to paren") + (provide 'custom-line-paragraph) ;;; custom-line-paragraph.el ends here. diff --git a/modules/custom-misc.el b/modules/custom-misc.el deleted file mode 100644 index 7e5e4f8d6..000000000 --- a/modules/custom-misc.el +++ /dev/null @@ -1,196 +0,0 @@ -;;; custom-misc.el --- Miscellaneous utility functions -*- coding: utf-8; lexical-binding: t; -*- - -;;; Commentary: -;; -;; Layer: 2 (Core UX). -;; Category: L/C. -;; Load shape: eager. -;; Eager reason: registers its C-; command bindings and an align-regexp advice -;; at load. Currently eager by init order; a deferral candidate for Phase 3/4. -;; Top-level side effects: advises align-regexp; binds several commands directly -;; under C-; (")", "f", "A", "SPC", "|", and others). -;; Runtime requires: keybindings. -;; Direct test load: yes (requires keybindings explicitly). -;; -;; This module provides various utility functions for text manipulation, -;; formatting, and navigation. Features include: -;; - Jump between matching delimiters -;; - Format regions/buffers (untabify, reindent, remove trailing whitespace) -;; - Word counting with region awareness -;; - Fraction glyph conversion (¼ ↔ 1/4) -;; - Force align-regexp to use spaces instead of tabs -;; -;; All functions are bound to the cj/custom-keymap for easy access. -;; -;;; Code: - -(require 'keybindings) ;; provides cj/custom-keymap - -(defun cj/jump-to-matching-paren () - "Jump to the matching delimiter if point is on (or just after) one. -If not on a delimiter, show a message. Respects the current syntax table." - (interactive) - (let* ((ca (char-after)) - (cb (char-before)) - ;; Check if on opening paren - (open-p (and ca (eq (char-syntax ca) ?\())) - ;; Check if on or just after closing paren - (close-p (or (and ca (eq (char-syntax ca) ?\))) - (and cb (eq (char-syntax cb) ?\)))))) - (cond - ;; Jump forward from opening - (open-p - (condition-case err - (forward-sexp) - (scan-error - (message "No matching delimiter: %s" (error-message-string err))))) - ;; Jump backward from closing - (close-p - (condition-case err - (backward-sexp) - (scan-error - (message "No matching delimiter: %s" (error-message-string err))))) - ;; Not on delimiter - (t - (message "Point is not on a delimiter."))))) - - -(defun cj/--format-region (start end) - "Internal implementation: Reformat text between START and END. -START and END define the region to operate on. -Replaces tabs with spaces, reindents, and deletes trailing whitespace." - (when (> start end) - (error "Invalid region: start (%d) is greater than end (%d)" start end)) - (save-excursion - (save-restriction - (narrow-to-region start end) - (untabify (point-min) (point-max)) - (indent-region (point-min) (point-max)) - (delete-trailing-whitespace (point-min) (point-max))))) - -(defun cj/format-region-or-buffer () - "Reformat the region or the entire buffer. -Replaces tabs with spaces, deletes trailing whitespace, and reindents." - (interactive) - (let ((start-pos (if (use-region-p) (region-beginning) (point-min))) - (end-pos (if (use-region-p) (region-end) (point-max)))) - (cj/--format-region start-pos end-pos) - (message "Formatted %s" (if (use-region-p) "region" "buffer")))) - -(defun cj/switch-to-previous-buffer () - "Switch to previously open buffer. -Repeated invocations toggle between the two most recently open buffers." - (interactive) - (switch-to-buffer (other-buffer (current-buffer) 1))) - -(defun cj/--count-words (start end) - "Internal implementation: Count words between START and END. -START and END define the region to count. -Returns the word count as an integer." - (when (> start end) - (error "Invalid region: start (%d) is greater than end (%d)" start end)) - (count-words start end)) - -(defun cj/count-words-buffer-or-region () - "Count the number of words in the buffer or region. -Display the result in the minibuffer." - (interactive) - (let* ((use-region (use-region-p)) - (begin (if use-region (region-beginning) (point-min))) - (end (if use-region (region-end) (point-max))) - (area-type (if use-region "the region" "the buffer")) - (word-count (cj/--count-words begin end))) - (message "There are %d words in %s." word-count area-type))) - -(defun cj/--count-characters (start end) - "Internal implementation: Count characters between START and END. -START and END define the region to count. -Returns the character count as an integer." - (when (> start end) - (error "Invalid region: start (%d) is greater than end (%d)" start end)) - (- end start)) - -(defun cj/count-characters-buffer-or-region () - "Count the number of characters in the buffer or region. -Display the result in the minibuffer." - (interactive) - (let* ((use-region (use-region-p)) - (begin (if use-region (region-beginning) (point-min))) - (end (if use-region (region-end) (point-max))) - (area-type (if use-region "the region" "the buffer")) - (char-count (cj/--count-characters begin end))) - (message "There are %d characters in %s." char-count area-type))) - - -(defun cj/--replace-fraction-glyphs (start end to-glyphs) - "Internal implementation: Replace fraction glyphs or text between START and END. -START and END define the region to operate on. -TO-GLYPHS when non-nil converts text (1/4) to glyphs (¼), -otherwise converts glyphs to text." - (when (> start end) - (error "Invalid region: start (%d) is greater than end (%d)" start end)) - (let ((replacements (if to-glyphs - '(("1/4" . "¼") - ("1/2" . "½") - ("3/4" . "¾") - ("1/3" . "⅓") - ("2/3" . "⅔")) - '(("¼" . "1/4") - ("½" . "1/2") - ("¾" . "3/4") - ("⅓" . "1/3") - ("⅔" . "2/3")))) - (count 0) - (end-marker (copy-marker end))) - (save-excursion - (dolist (r replacements) - (goto-char start) - (while (search-forward (car r) end-marker t) - (replace-match (cdr r)) - (setq count (1+ count))))) - count)) - -(defun cj/replace-fraction-glyphs (start end) - "Replace common fraction glyphs between START and END. -Operate on the buffer or region designated by START and END. -Replace the text representations with glyphs when called with a -\\[universal-argument] prefix." - (interactive (if (use-region-p) - (list (region-beginning) (region-end)) - (list (point-min) (point-max)))) - (let ((count (cj/--replace-fraction-glyphs start end current-prefix-arg))) - (message "Replaced %d fraction%s" count (if (= count 1) "" "s")))) - -(defun cj/align-regexp-with-spaces (orig-fun &rest args) - "Call ORIG-FUN with ARGS while temporarily disabling tabs for alignment. -This advice ensures =align-regexp' uses spaces by binding =indent-tabs-mode' -to nil." - (let ((indent-tabs-mode nil)) - (apply orig-fun args))) - -;; avoid double advice stacking in case the file is reloaded -(advice-remove 'align-regexp #'cj/align-regexp-with-spaces) -(advice-add 'align-regexp :around #'cj/align-regexp-with-spaces) - -(cj/register-command ")" #'cj/jump-to-matching-paren) -(cj/register-command "f" #'cj/format-region-or-buffer) -(cj/register-command "# w" #'cj/count-words-buffer-or-region) -(cj/register-command "# c" #'cj/count-characters-buffer-or-region) -(cj/register-command "/" #'cj/replace-fraction-glyphs) -(cj/register-command "A" #'align-regexp) -(cj/register-command "SPC" #'cj/switch-to-previous-buffer) -(cj/register-command "|" #'display-fill-column-indicator-mode) - -(with-eval-after-load 'which-key - (which-key-add-key-based-replacements - "C-; )" "jump to paren" - "C-; f" "format buffer" - "C-; # w" "count words" - "C-; # c" "count characters" - "C-; /" "fraction glyphs" - "C-; A" "align regexp" - "C-; SPC" "prev buffer" - "C-; |" "fill column")) - -(provide 'custom-misc) -;;; custom-misc.el ends here diff --git a/modules/custom-text-transform.el b/modules/custom-text-transform.el new file mode 100644 index 000000000..537f8df21 --- /dev/null +++ b/modules/custom-text-transform.el @@ -0,0 +1,63 @@ +;;; custom-text-transform.el --- Text glyph transforms -*- coding: utf-8; lexical-binding: t; -*- + +;;; Commentary: +;; +;; Layer: 2 (Core UX). +;; Category: L. +;; Load shape: eager. +;; Eager reason: registers its C-; / command binding at load. +;; Top-level side effects: binds cj/replace-fraction-glyphs under C-; /. +;; Runtime requires: keybindings. +;; Direct test load: yes (requires keybindings explicitly). +;; +;; Convert between text fractions (1/4) and their Unicode glyphs (1/4 becomes +;; the vulgar-fraction character), over the region or whole buffer. Split out +;; of the former custom-misc.el grab-bag. + +;;; Code: + +(require 'keybindings) ;; provides cj/register-command + +(defun cj/--replace-fraction-glyphs (start end to-glyphs) + "Internal implementation: Replace fraction glyphs or text between START and END. +START and END define the region to operate on. +TO-GLYPHS when non-nil converts text (1/4) to glyphs (¼), +otherwise converts glyphs to text." + (when (> start end) + (error "Invalid region: start (%d) is greater than end (%d)" start end)) + (let ((replacements (if to-glyphs + '(("1/4" . "¼") + ("1/2" . "½") + ("3/4" . "¾") + ("1/3" . "⅓") + ("2/3" . "⅔")) + '(("¼" . "1/4") + ("½" . "1/2") + ("¾" . "3/4") + ("⅓" . "1/3") + ("⅔" . "2/3")))) + (count 0) + (end-marker (copy-marker end))) + (save-excursion + (dolist (r replacements) + (goto-char start) + (while (search-forward (car r) end-marker t) + (replace-match (cdr r)) + (setq count (1+ count))))) + count)) + +(defun cj/replace-fraction-glyphs (start end) + "Replace common fraction glyphs between START and END. +Operate on the buffer or region designated by START and END. +Replace the text representations with glyphs when called with a +\\[universal-argument] prefix." + (interactive (if (use-region-p) + (list (region-beginning) (region-end)) + (list (point-min) (point-max)))) + (let ((count (cj/--replace-fraction-glyphs start end current-prefix-arg))) + (message "Replaced %d fraction%s" count (if (= count 1) "" "s")))) + +(cj/register-command "/" #'cj/replace-fraction-glyphs "fraction glyphs") + +(provide 'custom-text-transform) +;;; custom-text-transform.el ends here diff --git a/modules/custom-whitespace.el b/modules/custom-whitespace.el index cbf3eff12..52cc4e54d 100644 --- a/modules/custom-whitespace.el +++ b/modules/custom-whitespace.el @@ -228,5 +228,21 @@ Operate on the active region designated by START and END." "C-; w t" "untabify" "C-; w T" "tabify")) +;; --- align-regexp space enforcement + alignment/fill bindings --- +;; (formerly in custom-misc.el) +(defun cj/align-regexp-with-spaces (orig-fun &rest args) + "Call ORIG-FUN with ARGS while temporarily disabling tabs for alignment. +This advice ensures =align-regexp' uses spaces by binding =indent-tabs-mode' +to nil." + (let ((indent-tabs-mode nil)) + (apply orig-fun args))) + +;; avoid double advice stacking in case the file is reloaded +(advice-remove 'align-regexp #'cj/align-regexp-with-spaces) +(advice-add 'align-regexp :around #'cj/align-regexp-with-spaces) + +(cj/register-command "A" #'align-regexp "align regexp") +(cj/register-command "|" #'display-fill-column-indicator-mode "fill column") + (provide 'custom-whitespace) ;;; custom-whitespace.el ends here. |
