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.

Lorem Ipsum
  • 4,327
  • 2
  • 14
  • 35
  • `yas-after-exit-snippet-hook` should be sufficient if you can recognize the snippet from its expansion. – Tobias Feb 22 '19 at 16:39

2 Answers2

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.

npostavs
  • 9,033
  • 1
  • 21
  • 53
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.

Lorem Ipsum
  • 4,327
  • 2
  • 14
  • 35
  • 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 '19 at 17:32
  • 2
    This answer can be improved by adjusting the sarcasm level. Very nice explanation, otherwise! Upvoted! – mihai Jul 15 '21 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. – Lorem Ipsum Jul 15 '21 at 14:47