~protesilaos/substitute

e4fb1c4e4a953935be3fdb8982936875e345d981 — Protesilaos Stavrou 1 year, 10 months ago b44eaab
Make it possible to have fixed-case substitutions

Thanks to revrari for bringing this matter to my attention in issue 4
on the GitHub mirror: <https://github.com/protesilaos/substitute/issues/4>.
2 files changed, 62 insertions(+), 12 deletions(-)

M README.org
M substitute.el
M README.org => README.org +23 -2
@@ 99,6 99,22 @@ scope are as follows:
4. ~substitute-target-above-point~: Substitute the target from point
   to the beginning of the buffer (alias ~substitute-target-to-beginning-of-buffer~).

[ The ~substitute-fixed-letter-case~ and the =FIXED-CASE= prefix
  argument for all commands is part of {{{development-version}}}. ]

#+vindex: substitute-fixed-letter-case
All of the aforementioned commands accept an optional prefix argument
(=C-u= with the default key bindings).  This passes the =FIXED-CASE=
argument, which means that the substitution will not try to preserve
the letter casing of the target text.  Concretely, targeting =test=
and =TEST= with a substitute of =test-new= will make both of those
lower case.  Whereas without the prefix argument the result would be
=test-new= and =TEST-NEW=.  In many cases this is the desired effect.
To always have fixed letter casing, set the user option
~substitute-fixed-letter-case~ to a non-nil value.  Doing so is the
same as always calling the aforementioned commands with a prefix
argument.

#+vindex: substitute-post-replace-functions
#+findex: substitute-report-operation
Every substitution triggers the ~substitute-post-replace-functions~.


@@ 172,9 188,14 @@ Everything is in place to set up the package.
#+begin_src emacs-lisp
(require 'substitute)

;; If you do not like visual feedback on the matching target.  Default is t.
;; Set this to nil if you do not like visual feedback on the matching
;; target.  Default is t.
(setq substitute-highlight nil)

;; Set this to t if you want to always treat the letter casing
;; literally.
(setq substitute-fixed-letter-case t)

;; If you want a message reporting the matches that changed in the
;; given context.  We don't do it by default.
(add-hook 'substitute-post-replace-functions #'substitute-report-operation)


@@ 202,7 223,7 @@ matters.
+ Contributions to code or the manual :: Ed Tavinor, Kostas Andreadis,
  Wang Chunye.

+ Ideas and/or user feedback :: Tomasz Hołubowicz.
+ Ideas and/or user feedback :: Tomasz Hołubowicz, revrari.

* COPYING
:PROPERTIES:

M substitute.el => substitute.el +39 -10
@@ 51,6 51,18 @@ of the substitution."
  :group 'substitute
  :type 'boolean)

(defcustom substitute-fixed-letter-case nil
  "If non-nil, do not alter the letter case of the substituted text.
Otherwise try to perform capitalization or upcasing based on the
target text (per `replace-match').

Instead of setting this user option, users can invoke the
substitution commands, such as `substitute-target-in-buffer',
with a universal prefix argument."
  :package-version '(substitute . "0.2.0")
  :group 'substitute
  :type 'boolean)

(define-obsolete-variable-alias
  'substitute-post-replace-hook
  'substitute-post-replace-functions


@@ 215,10 227,15 @@ Each entry is a list of the symbol and its buffer positions.")
                                             (nth 2 target)))
                targets)))))

(defun substitute--replace-targets (sub &optional scope)
(defun substitute--replace-targets (sub &optional scope fixed)
  "Replace `substitute--last-matches' target with SUB.
If optional SCOPE is equal to `above', then adjust for a reverse
motion."
motion.

With optional FIXED as a non-nil value, do not alter the case of
the substituted text.  Otherwise perform capitalization or
upcasing based on the target text.  See the documenation of
`replace-match' for how this works."
  (when-let ((targets substitute--last-matches))
    (save-excursion
      (when (listp buffer-undo-list)


@@ 233,15 250,17 @@ motion."
                    (funcall
                     (if reverse 're-search-backward 're-search-forward)
                     (car target))
                    (replace-match sub)))
                    (replace-match sub (or fixed substitute-fixed-letter-case))))
                targets)))))

(defun substitute--operate (target sub &optional scope)
  "Operate on TARGET with SUB in SCOPE."
(defun substitute--operate (target sub &optional scope fixed)
  "Operate on TARGET with SUB in SCOPE.
Optional FIXED does not alter the letter casing of substituted
text (also see `substitute-fixed-letter-case')."
  (let* ((targets (or substitute--last-matches
                      (substitute--collect-targets target scope)))
         (count (length targets)))
    (substitute--replace-targets sub scope)
    (substitute--replace-targets sub scope fixed)
    (setq-local substitute--last-matches nil)
    (run-hook-with-args 'substitute-post-replace-hook
                        target sub count


@@ 264,7 283,7 @@ Report a `user-error' if no target is found."

(defmacro substitute-define-substitute-command (fn doc &optional scope)
  "Produce substitute command using FN, DOC, and SCOPE."
  `(defun ,fn (target sub)
  `(defun ,fn (target sub &optional fixed-case)
     ,(format
       "Substitute TARGET with SUB %s.



@@ 272,12 291,22 @@ When called interactively, TARGET is the symbol at point and SUB
is a string that is provided at the minibuffer prompt.

If the region is active, TARGET is the text within the region's
boundaries." doc)
boundaries.

With optional FIXED-CASE as a prefix argument, do not try to
preserve the letter casing of the target text: the substitution
is literal.  Otherwise try to preserve the case (per
`replace-match').

Instead of the optional FIXED-CASE argument, the user can set the
option `substitute-fixed-letter-case' to non-nil.  That is the
same as always calling this command with FIXED-CASE." doc)
     (interactive
      (let ((target (substitute--determine-target)))
        (list target
              (substitute--prompt target ,scope))))
     (substitute--operate target sub ,scope)))
              (substitute--prompt target ,scope)
              current-prefix-arg)))
     (substitute--operate target sub ,scope fixed-case)))

;;;###autoload (autoload 'substitute-target-in-buffer "substitute")
(substitute-define-substitute-command