Post-Candide

After the garden...


Preventing spurious yas-snippet expanding

Taming yasnippet-snippets with yas-buffer-local-condition #

If you use yasnippet-snippets alongside corfu, you may have run into this: you type a prefix like hi-l (heading for hi-lock-mode), press TAB expecting a completion popup, and instead get a let snippet expanded in your buffer.

Why it happens #

yasnippet-snippets ships hundreds of snippets with short, common keys. The problem is that word-separator characters like - break Emacs’s word boundaries, so the l after hi- is seen as a fresh word. Yasnippet happily matches it against its l or let key and expands before corfu gets a chance.

The naive fix is to add a # condition: guard directly in the offending snippet file — something like:

# condition: (not (member (char-before (- (point) 1)) '(?- ?_ ?/)))

This works for one snippet, but yasnippet-snippets has many short keys and you don’t want to patch them individually (nor maintain that across updates).

A better solution: yas-buffer-local-condition #

Yasnippet provides yas-buffer-local-condition, a Lisp form evaluated before every expansion. If it returns nil, the snippet is suppressed. Setting its default value globally is the clean solution:

(setq-default yas-buffer-local-condition
  '(let* ((key (and yas--current-template
                    (yas--template-key yas--current-template)))
           (char-before-key (and key (char-before (- (point) (length key))))))
     (or (null char-before-key)
         (not (memq (char-syntax char-before-key) '(?w ?_))))))

Basically, look at the character immediately before the matched snippet key. If it has word (?w) or symbol (?_) syntax, suppress the expansion. This covers -, _, /, :, & and anything else the current mode’s syntax table treats as a word or symbol constituent.

Because it uses the syntax table rather than a fixed set of characters, it adapts to whatever mode you’re in without extra configuration.

I wonder why it’s not a default, I can’t think of a good reason to not prevent this behaviour.

What it doesn’t break #