Blob


1 Snippets are small templates that can be “expanded”, and are generally used to avoid typing scaffolding. Since they are a common features available on various editors and IDEs, I have confidence the reader has seen them at some point.
3 Yasnippet is an Emacs package to manage and expand snippets; actually, yasnippet is probably THE Emacs package for snippets.
5 => https://github.com/joaotavora/yasnippet Yasnippet repository
7 (the full setup is at the end of the post)
9 By default, yasnippet will expand the snippets when the TAB key is pressed. I found this to be pretty inconvenient. The TAB key is already used to indent text and to trigger the ‘complete-at-point’, and it happened multiple times that instead of indenting or triggering the completions I expanded a snippet by accident. This had to be fixed.
11 Reading the yasnippet manual, I got this idea of using the space to trigger the snippet expansion. It may seem a bit crazy, but after playing a bit with it I felt that this was the way to go. To achieve it, one has to bind SPC to the ‘yas-maybe-expand’:
13 ``` elisp
14 (define-key yas-minor-mode-map (kbd "SPC") yas-maybe-expand)
15 ```
17 and remove the binding for TAB and ‘<tab>’ in ‘yas-minor-mode-map’.
19 But this isn’t enough, as you may find pretty soon. Let’s say that you have a snippet called ‘let’ that expands into a full let binding. That snippet gets expanded also when you type ‘let’ inside a comment or a string, and that’s not what you probably want.
21 To solve this issue, yasnippet provides a buffer-local variable ‘yas-buffer-local-condition’: it holds a lisp form that gets evaluated before the snippet expansion: if it evaluates to nil the expansion is cancelled (check the documentation for some other values it can return).
23 ``` elisp
24 (defun my/inside-comment-or-string-p ()
25 "T if point is inside a string or comment."
26 (let ((s (syntax-ppss)))
27 (or (nth 4 s) ;comment
28 (nth 3 s)))) ;string
29 ```
31 ‘syntax-ppss’ returns a list with a bunch of syntactical information. The 4th and 3rd field are if the point is inside a comment or inside a string (respectively).
33 By setting ‘yas-buffer-local-condition’ to the form (mind you, it’s a quoted list!) '(not (my/inside-comment-or-string-p)) you prevent yasnippet to expand when within comments or strings.
35 I’m using snippets mostly in LISPs buffers (elisp, common lisp, clojure), and there was still one thing that bothered me. I have a bunch of snippets for various special forms (defun/defn, let/let*, etc). Now, if by any chance I type a space after a preexisting ‘let’ (for instance) that let gets expanded AGAIN. So I have an additional condition to check that I’m not trying to expand something that is at the start of a list.
37 The full setup is this:
39 ``` elisp
40 (use-package yasnippet
41 :bind (:map yas-minor-mode-map
42 ("<tab>" . nil)
43 ("TAB" . nil))
44 :custom (yas-wrap-around-region t)
45 :config
46 (yas-global-mode +1)
47 (define-key yas-minor-mode-map (kbd "SPC") yas-maybe-expand)
49 (defun my/inside-comment-or-string-p ()
50 "T if point is inside a string or comment."
51 (let ((s (syntax-ppss)))
52 (or (nth 4 s) ;comment
53 (nth 3 s)))) ;string
55 (defun my/in-start-of-sexp-p ()
56 "T if point is after the first symbol in the list."
57 (save-excursion
58 (backward-char (length (current-word)))
59 (= ?\( (char-before))))
61 (defun my/yas-fix-local-condition ()
62 (setq yas-buffer-local-condition
63 '(not (or (my/inside-comment-or-string-p)
64 (my/in-start-of-sexp-p)))))
66 (mapcar (lambda (mode-hook)
67 (add-hook mode-hook #'my/yas-fix-local-condition))
68 '(emacs-lisp-mode-hook
69 lisp-interaction-mode-hook
70 clojure-mode-hook
71 c-mode-hook)))
72 ```
74 There’s still an issue that I’m not sure how to fix: prevent the expansion inside specific form. For instance, writing a ‘cl-loop’ it isn’t strange to type ‘while’, but it shouldn’t get expanded, but there are various situations where snippet shouldn’t be expandend, and since these are diverse and pretty rare, I’m not bothering (not for now at least). To prevent a snippet from expansion you can always type the space as C-q SPC.