1

I have a function that uses smartparens to replace specified pairs of parentheses.

(defun replace-paren-at-point (paren)
  (interactive "sReplace with: ")
  (if (looking-at "[「『《〈(〔【]")
    (progn
      (set-mark-command nil)
      (forward-sexp)
      (sp-wrap-with-pair paren)
      (sp-unwrap-sexp))
    (if (save-excursion
      (backward-char)
      (looking-at "[」』》〉)〕】]"))
    (progn
      (set-mark-command nil)
      (backward-sexp)
      (sp-wrap-with-pair paren)
      (sp-unwrap-sexp)))
    (message "No parenthesis at point.")))

One issue with the function above is that the user would be prompted to enter the replace string before he/she is reminded that there is "No parenthesis at point." (in cases which the cursor is at the wrong position.)

How do I change this so that an error message appears before the user is prompted for the input?

(defun replace-paren-at-point (paren)
  (interactive)
  (if (looking-at "[「『《〈(〔【]")
      (progn
        (read-string (format "Replace %s with: " (string (char-after))))
        (set-mark-command nil)
        (forward-sexp)
        (sp-wrap-with-pair paren)
        (sp-unwrap-sexp))
    (if (save-excursion
      (backward-char)
      (looking-at "[」』》〉)〕】]"))
    (progn
      (read-string (format "Replace %s with: " (string (char-before))))
      (set-mark-command nil)
      (backward-sexp)
      (sp-wrap-with-pair paren)
      (sp-unwrap-sexp)))
    (message "No parenthesis at point.")))

I try to use read-string for the input prompt but it doesn't work this way.

Seems like read-string has to be defined without the paren argument, but I need the paren variable set to store the value for the replace string. So I am kind of stuck.


Settings:

In case some of you might like to test the code above, here are the settings that should be evaluated in init.el before running the tests.

(package-initialize)
(smartparens-global-mode t)

(sp-pair "「" "」")
(sp-pair "『" "』")
(sp-pair "【" "】")
(sp-pair "《" "》")
(sp-pair "〈" "〉")
(sp-pair "(" ")")
(sp-pair "〔" "〕")

  • Can you also include some instruction on how to make this work? I don't think sp works with these special pairs out of the box. – jagrg Mar 13 at 13:20
1

Put a test for paren at point inside the interactive spec. If no paren at point, raise an error.

(defun replace-paren-at-point (paren)
  (interactive
   (if (not (or (looking-at "[「『《〈(〔【]")
                (save-excursion (backward-char) (looking-at "[」』》〉)〕】]"))))
       (error "No parenthesis at point")
     (list (read-string "Replace paren with: "))))
  (cond ((looking-at "[「『《〈(〔【]")
         (set-mark-command nil)
         (forward-sexp)
         (sp-wrap-with-pair paren)
         (sp-unwrap-sexp))
        ((save-excursion (backward-char) (looking-at "[」』》〉)〕】]"))
         (set-mark-command nil)
         (backward-sexp)
         (sp-wrap-with-pair paren)
         (sp-unwrap-sexp))))

(I didn't test the parts of your code. I just rearranged them. Assuming that your tests etc. do what you want, they should do that in this code too.)

| improve this answer | |
  • It works, thanks! – Sati Mar 13 at 4:29
  • Why does read-sting need to be nested within a (list (read-string "Replace paren with: "))? – Sati Mar 13 at 4:29
  • 2
    C-h f interactive, and check the Emacs manual about interactive (i interactive in the manual). When passed a sexp to evaluate, instead of a special "interactive" string, the sexp must return a list of all arguments you want to pass to the function. – Drew Mar 13 at 15:12
1

You can do the same using sp functions. No need to hardcode the matching pairs.

(defun replace-paren-at-point ()
  (interactive)
  (cond ((sp--looking-at (sp--get-opening-regexp))
         (sp-down-sexp)
         (call-interactively 'sp-rewrap-sexp))
        ((sp--looking-back (sp--get-closing-regexp))
         (sp-backward-sexp)
         (sp-down-sexp)
         (call-interactively 'sp-rewrap-sexp))
        (t
         (user-error "No matching pair at point"))))
| improve this answer | |
  • That's super cool! – Sati Mar 14 at 1:37
  • When I try to rewrap with using your code, I get the following error. user-error: Impossible pair prefix selected: \30024Error during redisplay: (eval (ergoemacs-status--eval)) signaled (error "Lisp nesting exceeds ‘max-lisp-eval-depth’") – Sati Mar 14 at 1:43
  • Works for me, although with emacs -Q I have to either reload the major-mode or use sp-local-pair. For example: (sp-local-pair 'lisp-interaction-mode "〔" "〕"). – jagrg Mar 14 at 11:18

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

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