8

Q: how can I tweak smartparens's decision rules for when to insert a paired or single character?

smartparens seems to be mostly smart when inserting single quotes (e.g., in text-mode or org-mode). Hence, it default to inserting a pair of 's and placing the cursor in between them, but, when invoked at the end of a word, only inserts a single ' in anticipation of contractions. So, e.g.:

  • ' => '*' (where * is point)
  • can + ' => can'* (so I can continue to write "can't")

However, there's a hiccup I'm looking to fix. I often use abbreviations that end in a period, but would like to make them possessive. For example, I might write "s.o." as an abbreviation for "someone," and therefore "s.o.'s" would be "someone's." The problem is that smartparens inserts a paired-' after a period:

  • what I want: s.o. + ' => s.o.'*
  • what I get: s.o. + ' => s.o.'*'

My specific quibble is with smartparens's behavior on ', but I can imagine this being a more general issue with other characters as well.

So: how can I tweak smartparens's decision rule to expand the set of characters after which it only inserts a single '?

PS: amusingly, trying to type "`smartparens`'s" in emacs gives the same irritating behavior.

Dan
  • 32,584
  • 6
  • 98
  • 168

2 Answers2

2

Yes, with pairing conditionals:

;; a sample from my .emacs.d
(defun my-sp-pair-function (id action context)
  (if (eq action 'insert)
    ;; t to pair, nil to not pair
    (or (looking-at "[[:space:][:punct:]]")
      (sp-point-before-eol-p id action context))
    t))

(with-eval-after-load 'smartparens
    (sp-pair "(" ")" :when '(my-sp-pair-function) :wrap "C-)")
    (sp-pair "{" "}" :when '(my-sp-pair-function) :wrap "C-}")
    (sp-pair "[" "]" :when '(my-sp-pair-function) :wrap "C-]")
    (sp-pair "\"" "\"" :when '(my-sp-pair-function) :wrap "C-\"")
    (sp-pair "'" "'" :when '(my-sp-pair-function)))
PythonNut
  • 10,243
  • 2
  • 29
  • 75
  • 1
    The lower part looks totally unrelated. The upper part looks like it won't do anything on its own. Could you please turn your answer into something functional? – wasamasa Jan 03 '15 at 13:32
  • That is true... copy-paste fail. Fixed. – PythonNut Jan 04 '15 at 03:39
  • Thanks for the thoughts, but `my-sp-pair-function` does not appear to be providing the desired functionality. It's giving me the same behavior as before after punctuation (paired insert), but now it also does a paired-insert after alphanumeric characters as well, breaking the contractions. – Dan Jan 04 '15 at 12:49
  • @Dan, this was meant to be a general solution. You can, for example replace the condition with this: `(and (or (looking-at "[[:space:][:punct:]]") (sp-point-before-eol-p id action context)) (not (looking-back "[.\`]")))` – PythonNut Jan 05 '15 at 01:56
2

You can modify smartparen's behavior by using the functions sp-pair and sp-local-pair. The smartparens wiki explains these functions in detail.

Basically you can something like following to customize the behavior globally

(sp-pair "\"" nil :unless '(my-custom-predicate))

OR to customize the behavior just for some modes

(sp-local-pair desired-modes "\"" nil :unless '(my-custom-predicate))

where desired-mode is the mode for which you want to customize the behavior and my-custom-predicate is function that smartparens should use to determine whether it should insert the pair automatically.

From sp-pair's docstring the custom predicate should accept

opening delimiter (which uniquely determines the pair), action and context. The context argument can have values:

  • string - if point is inside string.
  • comment - if point is inside comment.
  • code - if point is inside code. This context is only recognized in programming modes that define string semantics.

So a custom function that can be used as a :unless predicate in your specific case would be something like the following

(defun predp (id action context)
  (sp--looking-back-p "[[:punct:]]'"))

Notice that I am checking for the regex <punctuation>' since the point would be at .'| when the predicate is executing.

Finally, you can hook it into smartparens by doing

(sp-pair  "'" nil :unless'(predp))
Iqbal Ansari
  • 7,468
  • 1
  • 28
  • 31
  • Thanks, that gets me part of the way there, but could you provide a concrete example? I've tried various forms of the predicate function (eg,`(defun predp () (sp--looking-back-p "[[:space:][:punct:]]"))`), but `smartparens` ignores them all. – Dan Jan 03 '15 at 16:12
  • Hi @Dan, I have added the solution for your specific problem to the answer. You were not accepting the required arguments in the predicate, also the regex used was slightly off – Iqbal Ansari Jan 05 '15 at 17:21