6

I have a workflow that goes like, 1) use a snippet then 2) call a function.

What a glorious opportunity to automate! But how?


The snippet ipdb inserts import ipdb; ipdb.set_trace(). It has definition:

# -*- mode: snippet -*-
# name: ipdb
# key: ipdb
# --
import ipdb; ipdb.set_trace()

The function bm-toggle, from the lovely bm package, sets a visual bookmark on the current line.

The result of using each in sequence is a highlighted line indicating where my debug statement is:

enter image description here

I immediately think of hooks and see that there are indeed a variety of yas hooks. However, these hooks appear to be general; I only want to call a hook on a specific snippet.

1
  • yas-after-exit-snippet-hook should be sufficient if you can recognize the snippet from its expansion.
    – Tobias
    Feb 22, 2019 at 16:39

2 Answers 2

9

You can make snippet-specific bindings with # expand-env:

# -*- mode: snippet -*-
# name: ipdb
# key: ipdb
# expand-env: ((yas-after-exit-snippet-hook #'bm-toggle))
# --
import ipdb; ipdb.set_trace()

See also yasnippet#702.

3

Solution 1

(add-hook 'yas-after-exit-snippet-hook
          (lambda () (if (buffer-substring yas-snippet-beg yas-snippet-end)
                         (bm-toggle))))

The yas-after-exit-snippet-hook runs in an environment where yas-snippet-beg and yas-snippet-end are defined. These give the beginning and end points (i.e. region) of the inserted snippet.

The function buffer-substring returns the contents of a given region within the current buffer. We can use this with yas-snippet-beg and yas-snippet-end. If the snippet worked, then there will be text in the region. We expect the snippet to either work or fail gracefully, not defining the snippet region or running the hook. Anything non-nil evaluates to t. So, when we insert a snippet, the conditional will trigger and run the toggle.

Since it's unlikely we'd use this functionality anywhere else, there's no point defining an explicit function. The use of lambda creates an anonymous function. This function is added to the hook. When the hook is called, our function is created, called, and destroyed.

Solution 2

Another possibility is to search for the string explicitly.

(add-hook 'yas-after-exit-snippet-hook
          (lambda () (if (string-match-p (regexp-quote "import ipdb; ipdb.set_trace()") (thing-at-point 'line t))
                         (bm-toggle))))

This code checks for the thing-at-point on the current line. A quirk is that (thing-at-point 'line) returns a string with a newline at the end. So, instead of matching against the snippet "character-for-character", we can match it with a regex and accept any line containing the snippet.

3
  • 1
    You could use (buffer-substring yas-snippet-beg yas-snippet-end) instead of the thing-at-point-thing. (See the doc of yas-after-exit-snippet-hook.)
    – Tobias
    Feb 22, 2019 at 17:32
  • 2
    This answer can be improved by adjusting the sarcasm level. Very nice explanation, otherwise! Upvoted!
    – mihai
    Jul 15, 2021 at 12:01
  • 2
    @mihai Thank you for pointing that out. Those words were directed toward myself at the time of writing. Looking at my response several years later with the help of your perspective, I see how unhelpful those comments are, toward myself and others. I've removed them. Jul 15, 2021 at 14:47

Your Answer

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

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