I'm almost completely ignorant in elisp, but I'd like to have a function that does the following:

  1. Takes a string from (a) a region, if one is selected, or (b) user prompt, if a region is not selected.

  2. Internally replaces a couple of regexp in the string with other strings. (Specifically, first "[^a-zA-Z_- ]" with "", and then "[_ ]+" with "_").

  3. Saves the new string obtained from the replacement into the kill-ring or the clipboard.

Note that the original region is not changed.

My starting point was the piece of elisp below, taken from snips I've seen, to take care of 1.. Please note that I barely understand what this code actually does:

defun changestring (&optional arg)
  "Replace some character in string and save to kill-ring."
  (interactive "p")
  (if (use-region-p)
      (let ((region (buffer-substring-no-properties (region-beginning) (region-end))))
*** the replacing part should go here ***
    (let ((region (read-string "String to be converted: ")))
*** the replacing part should go here, again ***

I've been told that it can be slimmed down – indeed it looks like the replacement part is done twice.

Stackexchange seems to have some answers about saving to kill-ring, e.g. this and this, and modifying strings internally, e.g. this and this, but I really don't understand how to modify the code in those answers to this particular problem.

I wonder if anyone can help me, at least with all parts besides the regexp part, which I think I can modify myself. Of course a comment of your code is also greatly appreciated, so I can learn some elisp. Cheers!


1 Answer 1


One implementation might be this:

(defun changestring (&optional arg)
  "Replace some character in string and save to kill-ring."
  (interactive "p")
  ;; conditionally let variables and only execute the body if all of them are evaluated to non-nil
  ;; `str' is not an empty string
  ;; `regex1' and regex2 are not nil (obviously they wouldn't, they just strings)
  ;; `repl-str' is not nil after all the replacements
  (when-let* ((str (if (use-region-p)
                       (buffer-substring-no-properties (region-beginning) (region-end))
                     (read-string "String to be converted: ")))
              (regex1 "[_ ]")
              (regex2 "[^a-zA-Z_-]")
              ;; thread-last is like clojure's ->> threading macro, simply
              ;; speaking, it passes first argument to the last argument of the
              ;; following functions. `str' would be passed to
              ;; (replace-regexp-in-string regex1 "_" str) then result of that
              ;; function call would be passed to the next
              ;; replace-regexp-in-string.
              ;; this is equivalent to
              ;; (replace-regexp-in-string regex2 "" (replace-regexp-in-string regex1 "_" str))
              (repl-str (thread-last str
                                     (replace-regexp-in-string regex1 "_")
                                     (replace-regexp-in-string regex2 ""))))
    ;; create temp buffer, insert repl-str there and save whole temp buffer into clipboard
      (insert repl-str)
      (clipboard-kill-ring-save (point-min) (point-max)))))

Regexes are a bit different to yours and the order is different too -- otherwise first regex removes all spaces and the second one doesn't make sense (there are no spaces and underlines to replace after the first replace).



  • 1
    Fantastic! Thank you so much! Your comment makes sense, there's a little space in the brackets of the first regexp, I just fixed that. The rest of the script would have been quite difficult for me :)
    – pglpm
    Commented Oct 9, 2023 at 9:31
  • PS: very interesting the with-temp-buffer, never thought of something like that. Thank you for the explanation there and in the rest of the code.
    – pglpm
    Commented Oct 9, 2023 at 9:43
  • 1
    Sure, happy lisping!
    – Maxim Kim
    Commented Oct 9, 2023 at 9:59

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.