2

Say I have a snippets folder:

snippets/web-mode/
├── django
│   ├── else
│   ├── for
│   └── if
└── erb
    ├── else
    ├── for
    └── if

Both engines have different syntax ({% %} and {{ }} vs <% %> and <%= %>).

How do I load yasnippets of a certain web-mode engine when it's activated?


Versions:

GNU Emacs 26.1 (build 1, x86_64-pc-linux-gnu, GTK+ Version 3.22.30) of 2018-07-05
Spacemacs v.0.300.0 (develop, commit b00767cd6ec718df969bb1b7f713e5030ecf3c3e)

web-mode           20190301.1700
yasnippet          20181015.1212
  • How is the web-mode engine identified? Is it identified by the file name extension, such as .djhtml or .rhtml? – Tobias Apr 8 at 12:21
  • ERB (Embedded Ruby) usually has .erb.html but Django uses plain .html files. – Richard de Wit Apr 8 at 12:23
  • It would be nice if you could you test my solution and report whether it works for you. – Tobias Apr 9 at 14:09
  • Please add your Emacs version (M-x emacs-version), your web-mode version and your yas-version. You find your web-mode version and your yas-version in M-x package-list-packages. – Tobias Apr 10 at 7:06
  • @Tobias - versions added – Richard de Wit Apr 10 at 7:42
3
+100

The right place to use yas-activate-extra-mode is web-mode-on-engine-setted since that function also sets the engine specific snippets of web-mode.

Pityingly there is no hook so we have to add an advice. You can put the following into your init file.

After loading that init file (e.g., at emacs-restart) your engine-dependent yas-directory structure works.

(require 'web-mode)
(defcustom my-web-yas-mode-alist
  '(("erb" . erb)
    ("django" . django))
  ""
  :group 'web-mode
  :type '(repeat (cons
                  :tag "Map engine name to mode"
                  (string :tag "Engine Name")
                  (symbol :tag "Mode"))))

(defun my-web-setup-yas ()
  "Setup yas according to `web-mode-engine`."
  (require 'yasnippet)
  (let ((extra-mode (cdr (assoc-string web-mode-engine my-web-yas-mode-alist))))
    (when extra-mode
      (yas-activate-extra-mode extra-mode))))

(advice-add 'web-mode-on-engine-setted :after #'my-web-setup-yas)

Note that you should use the web-mode as yas-parent in each of your derived yasnippet modes (also those that do not exist as real major modes).
Correction: The snippets for web-mode are activated anyway since that is the major-mode of the buffer.

Note: If you want to keep your directory structure you must consider following items:

  • Use ~/.emacs.d/snippets/web-mode as one of the root-directories.

    • You can do that with the following code:

    (eval-after-load 'yasnippet
      '(let ((dir "~/.emacs.d/snippets/web-mode"))
          (add-to-list 'yas-snippet-dirs dir)
          (yas-load-directory dir)))
    
  • You cannot load snippets directly from the directory ~/.emacs.d/snippets/web-mode.

    • Otherwise the snippets in all sub-directories are also interpreted as snippets of web-mode.
    • You can avoid loading snippets from ~/.emacs.d/snippets/web-mode by putting a file .yas-skip in there.
    • If you have snippets for web-mode in ~/.emacs.d/snippets/web-mode you could use another directory, e.g., ~/.emacs.d/snippets/web-mode-engines for collecting the extra modes erb and django in that case you must adapt the Elisp code here and the location for .yas-skip to that new directory. Actually, you do not even need .yas-skip anymore since there is no mode named web-mode-engines that could be loaded.

If you do not like that hassle of the previous steps you need to move ~/.emacs.d/snippets/web-mode/erb and ~/.emacs.d/snippets/web-mode/django to ~/.emacs.d/snippets/ and afterwards reload the snippet directory ~/.emacs.d/snippets/.


Test conditions:

Running emacs -Q, version GNU Emacs 26.1 (build 2, i686-pc-linux-gnu, GTK+ Version 3.22.30) of 2018-05-29

Via package library installed relevant packages:

Reconstruction:

  • Start Emacs with emacs -Q
  • Run M-x package-initialize
  • Put the following snippets into their places:

File ~/.emacs.d/snippets/web-mode/erb/if:

  # -*- mode: snippet -*-
  # name: if statement
  # key: if
  # --
  <% if $1 %>
    $2
  <% end %>

File ~/.emacs.d/snippets/web-mode/erb/unless:

# -*- mode: snippet -*-
# name: unless statement
# key: unless
# --
<% unless $1 %>
  $2
<% end %>

File ~/.emacs.d/snippets/web-mode/django/if:

# -*- mode: snippet -*-
# name: if statement
# key: if
# --
{% if $1 %}
  $2
{% endif %}

File ~/.emacs.d/snippets/web-mode/django/with:

# -*- mode: snippet -*-
# name: with statement
# key: with
# --
{% with $1 %}
  $2
{% endwith %}
  • Put the above two Elisp source blocks into the *scratch* buffer and run M-x eval-buffer there.
  • Run M-x yas-global-mode.
  • Open the erb test file with the following content:
<!DOCTYPE HTML>
<html>
  <head>
  </head>
  <body>
      <ul>
      <% @products.each do |p| %>
          <li><%=  @p.name %></li>

      </ul>
  </body>
</html>
<!--
     Local Variables:
     mode: web
     eval: (web-mode-set-engine "erb")
     End:
-->
  • Test result:

    • The YASnippet menu now contains the submenus erb,prog-mode,nxml-mode,html-mode.
    • Inserting if and pressing TAB results into the expansion of the erb-if-statement.
    • The erb submenu contains the items if statement and unless statement.
  • Open the django test file with the following content:

<!DOCTYPE html>
<html>
    <head>
    <title>{{ title }}</title>
    </head>
    <body>
    <h1>About {{ title }}</h1>
    <p>This Website was developed by {{ author }}.</p>
    <p>Now using the Django's Template Engine.</p>
    <p><a href="{% url 'homepage' %}">Return to the homepage</a>.</p>

    </body>
</html>
<!--
     Local Variables:
     mode: web
     eval: (web-mode-set-engine "django")
     End:
-->
  • Test result:

    • The YASnippet menu now contains the submenus django,prog-mode,nxml-mode,html-mode.
    • The django submenu contains the items
    • Inserting if and pressing TAB results into the expansion of the django-if-statement.
    • The erb submenu contains the items if statement and with statement.
  • I've put a .yas-parents file containing the string web-mode in both django and erb folders, and put the code snippet in my init. They don't tab-complete, but only the django ones show up when I call yas-insert-snippet (in both engines, different projects). However, when I remove the code snippet, they all show up in yas-insert-snippet. – Richard de Wit Apr 10 at 6:34
  • I am using Spacemacs btw – Richard de Wit Apr 10 at 7:01
  • @RicharddeWit Note, that if the web-mode-engine is not recognized it must be set by M-x web-mode-set-engine. I will add a note in my answer how you can do that by file-local variables. – Tobias Apr 10 at 7:31
  • 2
    @RicharddeWit for this solution to work, I think you need the snippet under snippets/erb and snippet/django folders, not snippets/web-mode/erb and snippets/web-mode/django. – npostavs Apr 10 at 10:18
  • 1
    @RicharddeWit I am happy that your problem could be solved. Note, that even if you put the engine extra modes into the folder web-mode-engines it is advantigeous to add a .yas-skip file there. That avoids that the subdirectories of that folder are read twice. I will edit the answer "to preferentially put the snippets in their own web-mode-engines folder" when I have some spare time. – Tobias Apr 11 at 6:18

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

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