diff options
Diffstat (limited to 'modules/custom-text-enclose.el')
| -rw-r--r-- | modules/custom-text-enclose.el | 99 |
1 files changed, 34 insertions, 65 deletions
diff --git a/modules/custom-text-enclose.el b/modules/custom-text-enclose.el index fdfb92230..4d72347d1 100644 --- a/modules/custom-text-enclose.el +++ b/modules/custom-text-enclose.el @@ -1,4 +1,4 @@ -;;; custom-text-enclose.el --- -*- coding: utf-8; lexical-binding: t; -*- +;;; custom-text-enclose.el --- Wrap, unwrap, and prefix text ranges -*- coding: utf-8; lexical-binding: t; -*- ;;; Commentary: ;; @@ -12,23 +12,10 @@ ;; Runtime requires: keybindings (change-inner on demand via declare-function). ;; Direct test load: yes (requires keybindings explicitly). ;; -;; Text enclosure utilities for wrapping and line manipulation. +;; Text enclosure commands under C-; s. Commands wrap or unwrap the active +;; region/word at point, and add prefixes, suffixes, indentation, or dedentation +;; across selected lines. ;; -;; Wrapping functions: -;; - surround-word-or-region - wrap text with same delimiter on both sides -;; - wrap-word-or-region - wrap with different opening/closing delimiters -;; - unwrap-word-or-region - remove surrounding delimiters -;; -;; Line manipulation: -;; - append-to-lines - add suffix to each line -;; - prepend-to-lines - add prefix to each line -;; - indent-lines - add leading whitespace (spaces or tabs) -;; - dedent-lines - remove leading whitespace -;; -;; Most functions work on region or entire buffer when no region is active. -;; -;; Bound to keymap prefix C-; s - ;;; Code: (require 'keybindings) ;; provides cj/custom-keymap @@ -54,48 +41,42 @@ CLOSING is appended to TEXT. Returns the wrapped text without modifying the buffer." (concat opening text closing)) +(defun cj/--enclose-region-or-word (transform &optional no-target-message) + "Apply TRANSFORM to the active region or the word at point, in place. +TRANSFORM is a function of one string (the target text) returning the +replacement text. An active region is the target; otherwise the word at +point is. With neither, show NO-TARGET-MESSAGE (or a default) and leave the +buffer unchanged. Point is left after the inserted text." + (let ((bounds (cond ((use-region-p) (cons (region-beginning) (region-end))) + ((thing-at-point 'word) (bounds-of-thing-at-point 'word))))) + (if (null bounds) + (message "%s" (or no-target-message + "Can't do that. No word at point and no region selected.")) + (let* ((beg (car bounds)) + (end (cdr bounds)) + (text (buffer-substring beg end))) + (delete-region beg end) + (goto-char beg) + (insert (funcall transform text)))))) + (defun cj/surround-word-or-region () "Surround the word at point or active region with a string. The surround string is read from the minibuffer." (interactive) - (let ((str (read-string "Surround with: ")) - (regionp (use-region-p))) - (if regionp - (let ((beg (region-beginning)) - (end (region-end)) - (text (buffer-substring (region-beginning) (region-end)))) - (delete-region beg end) - (goto-char beg) - (insert (cj/--surround text str))) - (if (thing-at-point 'word) - (let* ((bounds (bounds-of-thing-at-point 'word)) - (text (buffer-substring (car bounds) (cdr bounds)))) - (delete-region (car bounds) (cdr bounds)) - (goto-char (car bounds)) - (insert (cj/--surround text str))) - (message "Can't insert around. No word at point and no region selected."))))) + (let ((str (read-string "Surround with: "))) + (cj/--enclose-region-or-word + (lambda (text) (cj/--surround text str)) + "Can't insert around. No word at point and no region selected."))) (defun cj/wrap-word-or-region () "Wrap the word at point or active region with different opening/closing strings. The opening and closing strings are read from the minibuffer." (interactive) (let ((opening (read-string "Opening: ")) - (closing (read-string "Closing: ")) - (regionp (use-region-p))) - (if regionp - (let ((beg (region-beginning)) - (end (region-end)) - (text (buffer-substring (region-beginning) (region-end)))) - (delete-region beg end) - (goto-char beg) - (insert (cj/--wrap text opening closing))) - (if (thing-at-point 'word) - (let* ((bounds (bounds-of-thing-at-point 'word)) - (text (buffer-substring (car bounds) (cdr bounds)))) - (delete-region (car bounds) (cdr bounds)) - (goto-char (car bounds)) - (insert (cj/--wrap text opening closing))) - (message "Can't wrap. No word at point and no region selected."))))) + (closing (read-string "Closing: "))) + (cj/--enclose-region-or-word + (lambda (text) (cj/--wrap text opening closing)) + "Can't wrap. No word at point and no region selected."))) (defun cj/--unwrap (text opening closing) "Internal implementation: Remove OPENING and CLOSING from TEXT if present. @@ -114,22 +95,10 @@ Returns the unwrapped text if both delimiters present, otherwise unchanged." The opening and closing strings are read from the minibuffer." (interactive) (let ((opening (read-string "Opening to remove: ")) - (closing (read-string "Closing to remove: ")) - (regionp (use-region-p))) - (if regionp - (let ((beg (region-beginning)) - (end (region-end)) - (text (buffer-substring (region-beginning) (region-end)))) - (delete-region beg end) - (goto-char beg) - (insert (cj/--unwrap text opening closing))) - (if (thing-at-point 'word) - (let* ((bounds (bounds-of-thing-at-point 'word)) - (text (buffer-substring (car bounds) (cdr bounds)))) - (delete-region (car bounds) (cdr bounds)) - (goto-char (car bounds)) - (insert (cj/--unwrap text opening closing))) - (message "Can't unwrap. No word at point and no region selected."))))) + (closing (read-string "Closing to remove: "))) + (cj/--enclose-region-or-word + (lambda (text) (cj/--unwrap text opening closing)) + "Can't unwrap. No word at point and no region selected."))) (defun cj/--append-to-lines (text suffix) "Internal implementation: Append SUFFIX to each line in TEXT. |
