6

Not infrequently, I want to change the delimiters of a string from single quotes to double quotes, or vice versa. This is so annoying a process I wonder if there's an easier way.

What I do now is:

  • delete the opening or closing quote
  • type the replacement quote
  • electric-pair-mode helpfully inserts a matching copy of the new delimiter, which must also be deleted
  • navigate to the other delimiter somehow (I can't just jump over the string with C-M-f or C-M-b because the delimiters no longer match)
  • delete the other quote
  • type the replacement quote
  • delete the copy of the replacement quote inserted by electric-pair-mode

It's kind of a pain. I could fashion some workaround in Elisp, but is there some easier and/or officially-blessed way to conveniently change quotes?

7 Answers 7

2

To continue this thread of alternatives...

A better workflow than the one you describe is to use electric-pair-mode the way it is meant to so that it works for you instead of against you:

  1. Place the cursor inside the quotes.
  2. Select the contents inside the quotes. One of the most convenient ways of doing this is to use Magnar's change-inner.el and bind change-inner to a convenient key.
  3. Cut the contents.
  4. Press backspace to remove the first quote and let electric-pair-mode to remove the second.
  5. Key in the replacement quote and let electric-pair-mode to add the other one. The cursor is now between the quotes.
  6. Paste in the cut text.
1
  • This answer gets me most of the way to what I need with packages I already use, so I've selected it. None of the solutions I looked at were quite smart enough to deal with internal quotes--turning "isn't" into 'isn\'t' for example--so I'll have to build something like that from scratch on my own if I want it, it seems.
    – Sean
    Commented Nov 18, 2017 at 23:37
3

I knew this was going to take some time, but in the end I couldn't resist the temptation to figure out how it's done properly. :) Enjoy!

(defun toggle-quotes (uarg)
  "Toggle single and double quotes around a string.

Does the change only if the point is inside quotes that are
syntactically correct under the current major mode. For example,
single quotes are not valid strings under elisp-mode. You can
force the change by using the universal argument C-u."
  (interactive "p")
  (let ((quotes-re "['\"]")
        (forward-char "")
        (new-quote-char "'"))
    (if (or (nth 3 (syntax-ppss)) (= uarg 4))
        (save-excursion
          (when (re-search-forward quotes-re nil t)
            (setq forward-char (match-string 0))
            (if (string-match forward-char "'")
                (setq new-quote-char "\""))
            (when (re-search-backward forward-char nil t 2)
              (goto-char (match-beginning 0))
              (replace-match new-quote-char)
              (re-search-forward quotes-re nil t)
              (replace-match new-quote-char))))
      (message "Point not inside a valid string! Universal argument overrides."))))
1
  • Looks like a good start, but it gets confused by internal quotes, for example "foo '' bar".
    – Sean
    Commented Nov 18, 2017 at 23:29
3

Try embrace. It’s easy to use and does what you want and more.

2
  • 1
    Could you provide more detail and an example on how to use it?
    – Dan
    Commented Nov 17, 2017 at 19:16
  • What Dan said. This is essentially a link-only answer. As such it risks being deleted. Please see emacs.stackexchange.com/help/how-to-ask.
    – Drew
    Commented Nov 18, 2017 at 0:07
1

https://github.com/andreas-roehler/thing-at-point-utils

comes with a

ar-delimited2.el

which provides for example a command ar-delimited2singlequoted

1

One option is to use Xah Lee's xah-change-bracket-pairs from http://ergoemacs.org/emacs/elisp_change_brackets.html.

To get you started, here are two simple interactive functions that use it to do what you want. Bind them to your key of choice. You have to first select the quoted text, for example by placing the cursor inside the quotes and pressing your key for er/expand-region from expand-region.el three times.

(defun toggle-quotes-to-single ()
  "Toggle quotes between single and double."
  (interactive)
  (let ((double "\"ascii quote\"")
        (single "‘single quote’"))
    (xah-change-bracket-pairs double single)))

(defun toggle-quotes-to-double ()
  "Toggle quotes between double and single."
  (interactive)
  (let ((double "\"ascii quote\"")
        (single "‘single quote’"))
    (xah-change-bracket-pairs single double)))

It would be even better to combine these functions to one and automatically select the closest quote pair and toggle it.

1

First of all, I disabled electric-pair-mode by:

(setq-default c-electric-flag nil)

To remove a pair of matching quotes/parens, we can:

  1. use expand-region to select the content without the quotes you wanna remove.
  2. copy the content.
  3. expand the region to include the pair of matching quotes/parens.
  4. paste the content.

To add a new pair of quotes/parens, I found following two related packages:

-2

If you are just trying to replace double quotes with single quotes can't you just do find and replace with M-S-%. Then replace " with ' and type ! to replace all occurrences of " with '?

1
  • I want to replace just a single matching pair of quotes, not all of them.
    – Sean
    Commented Nov 18, 2017 at 23:18

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.