diff mbox series

[bug#57069,v2] etc: Add tempel snippets.

Message ID 20220811060745.6892-1-ngraves@ngraves.fr
State Accepted
Headers show
Series [bug#57069,v2] etc: Add tempel snippets. | expand

Checks

Context Check Description
cbaines/comparison success View comparision
cbaines/git-branch success View Git branch
cbaines/applying patch success View Laminar job
cbaines/issue success View issue

Commit Message

Nicolas Graves Aug. 11, 2022, 6:07 a.m. UTC
* doc/contributing.texi:
  - adding configuration description for tempel snippets.
  - adapting yasnippet configuration to directory move.
* etc/snippets/tempel/*.eld: adding alternative to current yasnippets.
* etc/snippets*-mode: moved to /etc/snippets/yas/*-mode not to conflit with
tempel snippets.
---
 doc/contributing.texi                         |  17 ++-
 etc/snippets/tempel/scheme-mode.eld           | 113 ++++++++++++++++++
 etc/snippets/tempel/text-mode.eld             | 101 ++++++++++++++++
 .../{ => yas}/scheme-mode/guix-bzr-reference  |   0
 .../{ => yas}/scheme-mode/guix-cvs-reference  |   0
 .../{ => yas}/scheme-mode/guix-git-reference  |   0
 .../{ => yas}/scheme-mode/guix-hg-reference   |   0
 .../{ => yas}/scheme-mode/guix-origin         |   0
 .../{ => yas}/scheme-mode/guix-package        |   0
 .../{ => yas}/scheme-mode/guix-svn-reference  |   0
 .../guix-commit-message-add-cl-package        |   0
 .../text-mode/guix-commit-message-add-package |   0
 .../guix-commit-message-remove-package        |   0
 .../guix-commit-message-rename-package        |   0
 .../guix-commit-message-update-package        |   0
 .../guix-commit-message-use-https-home-page   |   0
 16 files changed, 226 insertions(+), 5 deletions(-)
 create mode 100644 etc/snippets/tempel/scheme-mode.eld
 create mode 100644 etc/snippets/tempel/text-mode.eld
 rename etc/snippets/{ => yas}/scheme-mode/guix-bzr-reference (100%)
 rename etc/snippets/{ => yas}/scheme-mode/guix-cvs-reference (100%)
 rename etc/snippets/{ => yas}/scheme-mode/guix-git-reference (100%)
 rename etc/snippets/{ => yas}/scheme-mode/guix-hg-reference (100%)
 rename etc/snippets/{ => yas}/scheme-mode/guix-origin (100%)
 rename etc/snippets/{ => yas}/scheme-mode/guix-package (100%)
 rename etc/snippets/{ => yas}/scheme-mode/guix-svn-reference (100%)
 rename etc/snippets/{ => yas}/text-mode/guix-commit-message-add-cl-package (100%)
 rename etc/snippets/{ => yas}/text-mode/guix-commit-message-add-package (100%)
 rename etc/snippets/{ => yas}/text-mode/guix-commit-message-remove-package (100%)
 rename etc/snippets/{ => yas}/text-mode/guix-commit-message-rename-package (100%)
 rename etc/snippets/{ => yas}/text-mode/guix-commit-message-update-package (100%)
 rename etc/snippets/{ => yas}/text-mode/guix-commit-message-use-https-home-page (100%)

Comments

Liliana Marie Prikler Aug. 11, 2022, 4:21 p.m. UTC | #1
Am Donnerstag, dem 11.08.2022 um 08:07 +0200 schrieb Nicolas Graves:
> * doc/contributing.texi:
>   - adding configuration description for tempel snippets.
>   - adapting yasnippet configuration to directory move.
Write full sentences, don't use pseudo-lists.
> * etc/snippets/tempel/*.eld: adding alternative to current
> yasnippets.
* etc/snippets/tempel/scheme-mode.eld: New file.
* etc/snippets/tempel/text-mode.eld: New file.

I'm not too sure about the suffix here.  I'd personally prefer a
suffix-less file with a mode-line.
> * etc/snippets*-mode: moved to /etc/snippets/yas/*-mode not to
Again, spell out both directories, as in:
* etc/snippets/scheme-mode: Moved from here...
* etc/snippets/yas/scheme-mode: ... to here.
> conflit with
Should be conflict, but I don't think this needs to be stated when
using the ChangeLog style as above.
> tempel snippets.
> ---
>  doc/contributing.texi                         |  17 ++-
>  etc/snippets/tempel/scheme-mode.eld           | 113
> ++++++++++++++++++
>  etc/snippets/tempel/text-mode.eld             | 101 ++++++++++++++++
>  .../{ => yas}/scheme-mode/guix-bzr-reference  |   0
>  .../{ => yas}/scheme-mode/guix-cvs-reference  |   0
>  .../{ => yas}/scheme-mode/guix-git-reference  |   0
>  .../{ => yas}/scheme-mode/guix-hg-reference   |   0
>  .../{ => yas}/scheme-mode/guix-origin         |   0
>  .../{ => yas}/scheme-mode/guix-package        |   0
>  .../{ => yas}/scheme-mode/guix-svn-reference  |   0
>  .../guix-commit-message-add-cl-package        |   0
>  .../text-mode/guix-commit-message-add-package |   0
>  .../guix-commit-message-remove-package        |   0
>  .../guix-commit-message-rename-package        |   0
>  .../guix-commit-message-update-package        |   0
>  .../guix-commit-message-use-https-home-page   |   0
>  16 files changed, 226 insertions(+), 5 deletions(-)
>  create mode 100644 etc/snippets/tempel/scheme-mode.eld
>  create mode 100644 etc/snippets/tempel/text-mode.eld
>  rename etc/snippets/{ => yas}/scheme-mode/guix-bzr-reference (100%)
>  rename etc/snippets/{ => yas}/scheme-mode/guix-cvs-reference (100%)
>  rename etc/snippets/{ => yas}/scheme-mode/guix-git-reference (100%)
>  rename etc/snippets/{ => yas}/scheme-mode/guix-hg-reference (100%)
>  rename etc/snippets/{ => yas}/scheme-mode/guix-origin (100%)
>  rename etc/snippets/{ => yas}/scheme-mode/guix-package (100%)
>  rename etc/snippets/{ => yas}/scheme-mode/guix-svn-reference (100%)
>  rename etc/snippets/{ => yas}/text-mode/guix-commit-message-add-cl-
> package (100%)
>  rename etc/snippets/{ => yas}/text-mode/guix-commit-message-add-
> package (100%)
>  rename etc/snippets/{ => yas}/text-mode/guix-commit-message-remove-
> package (100%)
>  rename etc/snippets/{ => yas}/text-mode/guix-commit-message-rename-
> package (100%)
>  rename etc/snippets/{ => yas}/text-mode/guix-commit-message-update-
> package (100%)
>  rename etc/snippets/{ => yas}/text-mode/guix-commit-message-use-
> https-home-page (100%)
> 
> diff --git a/doc/contributing.texi b/doc/contributing.texi
> index 02c7c5ae59..808d71e40a 100644
> --- a/doc/contributing.texi
> +++ b/doc/contributing.texi
> @@ -320,15 +320,22 @@ s-expression, etc.
>  @cindex reducing boilerplate
>  We also provide templates for common git commit messages and package
>  definitions in the @file{etc/snippets} directory.  These templates
> can
> -be used with @url{https://joaotavora.github.io/yasnippet/,
> YASnippet} to
> -expand short trigger strings to interactive text snippets.  You may
> want
> -to add the snippets directory to the @var{yas-snippet-dirs} variable
> in
> -Emacs.
> +be used to expand short trigger strings to interactive text
> snippets,
> +with either @url{https://joaotavora.github.io/yasnippet/, YASnippet}
> or
> +@url{https://github.com/minad/tempel/, Tempel}.  You may want to add
> the
> +@file{etc/snippets/yas} (respectively
> @file{etc/snippets/tempel/*.eld}
> +or selected files) snippets directory to the @var{yas-snippet-dirs}
> +(@var{tempel-path} resp.) variable in Emacs.
You probably want to reorganize this a little.

"We also provide templates for common git commit messages and package
definitions in the @file{etc/snippets} directory.  If you use 
YASnippet, you may want to <yasnippet configuration>.  If you use
Tempel, you may want to <tempel configuration>."

>  
>  @lisp
>  ;; @r{Assuming the Guix checkout is in ~/src/guix.}
> +;; @r{Yasnippet}
>  (with-eval-after-load 'yasnippet
> -  (add-to-list 'yas-snippet-dirs "~/src/guix/etc/snippets"))
> +  (add-to-list 'yas-snippet-dirs "~/src/guix/etc/snippets/yas"))
> +;;@r{Tempel}
> +(with-eval-after-load 'tempel
> +         (add-to-list 'tempel-path
> +         "~/src/guix/etc/snippets/tempel/*.eld"))
>  @end lisp
>  
>  The commit message snippets depend on @url{https://magit.vc/, Magit}
> to
> diff --git a/etc/snippets/tempel/scheme-mode.eld
> b/etc/snippets/tempel/scheme-mode.eld
> new file mode 100644
> index 0000000000..5fcd7030b8
> --- /dev/null
> +++ b/etc/snippets/tempel/scheme-mode.eld
> @@ -0,0 +1,113 @@
> +-*- mode: lisp-data -*-
> +
> +scheme-mode
> +
> +(package...
> + "(define-public " (s name) n>
> + "(package" n>
Style-wise, I think n> "(package" is better than the other way round.
You can also n  > and n    > on subsequent lines for clarity.

> + "(name \"" (s name) "\")" n>
> + "(version \"" p "\")" n>
> + "(source origin..." p ")" n>
I don't think you want that p here.
> + "(build-system \""
> + (p (completing-read "build-system:"
> +                     '("android-ndk"
> +                       "ant"
> +                       "asdf"
> +                       "cargo"
> +                       "chicken"
> +                       "clojure"
> +                       "cmake"
> +                       "copy"
> +                       "dub"
> +                       "dune"
> +                       "emacs"
> +                       "font"
> +                       "glib-or-gtk"
> +                       "gnu"
> +                       "go"
> +                       "guile"
> +                       "haskell"
> +                       "julia"
> +                       "linux-module"
> +                       "maven"
> +                       "meson"
> +                       "minetest"
> +                       "minify"
> +                       "node"
> +                       "ocaml"
> +                       "perl"
> +                       "python"
> +                       "qt"
> +                       "r"
> +                       "rakudo"
> +                       "rebar"
> +                       "renpy"
> +                       "ruby"
> +                       "scons"
> +                       "texlive"
> +                       "trivial"
> +                       "waf")
> +                     nil t) "-build-system")
I'd suggest skipping the completing-read and just (p "gnu").  Most
build systems should be easy enough to type without autocompletion.
> + "\")" n>
> + "(home-page \"" p "\")" n>
> + "(synopsis \"" p "\")" n>
> + "(description \"" p "\")" n>
> + "(license " p ")))" n)
> +
> +(origin...
> + "(origin" n>
> + "(method \""
> + (p (completing-read "method:"
> +                     '("url-fetch"
> +                       "url-fetch/tarbomb"
> +                       "url-fetch/zipbomb"
> +                       "cvs-fetch"
> +                       "git-fetch"
> +                       "hg-fetch"
> +                       "svn-fetch"
> +                       "bzr-fetch")
> +                     nil t) method) "\")" n>
Here, I think (p "url-fetch"), but (s "method") might also work.
> + "(uri " (cl-case (intern method)
> +           ('git-fetch "git-reference...")
> +           ('svn-fetch "svn-reference...")
> +           ('hg-fetch  "hg-reference...")
> +           ('cvs-fetch "cvs-reference...")
> +           ('bzr-fetch "bzr-reference...")
> +           (t          "(string-append \"https://"))
Will this cl-case be dynamically recomputed?  I wonder if we can get
the result of the previous p/s here...
> + p
> + (if (equal (substring method 0 9)  "url-fetch") "\")")
> + ")" n>
> + (cl-case (intern method)
> +   ('git-fetch "(file-name (git-file-name name version))\n")
> +   ('hg-fetch "(file-name (hg-file-name name version))\n")
> +   ('svn-fetch "(file-name (string-append name \"-\" version \"-
> checkout\"))\n")
> +   ('cvs-fetch "(file-name (string-append name \"-\" version \"-
> checkout\"))\n")
> +   ('bzr-fetch "(file-name (string-append name \"-\" version \"-
> checkout\"))\n")
> +   (t          "")) >
Rather than that I think adding a template (git-file-name...) which
expands to (file-name (git-file-name (p "name") (p "version")) and
variants for the others is a better idea.
> + "(sha256" n> "(base32 \"" p "\")))")
> +
> +(git-reference...
> + "(git-reference" n>
> + "(url \"" p "\")" n>
> + "(commit \"" p "\"))")
> +
> +(svn-reference...
> + "(svn-reference" n>
> + "(url \"" p "\")" n>
> + "(revision \"" p "\"))")
> +
> +(cvs-reference...
> + "(cvs-reference" n>
> + "(root-directory \"" p "\")" n>
> + "(module \"" p "\")" n>
> + "(revision \"" p "\"))")
> +
> +(hg-reference...
> + "(hg-reference" n>
> + "(url \"" p "\")" n>
> + "(changeset \"" p "\"))")
> +
> +(bzr-reference...
> + "(bzr-reference" n>
> + "(url \"" p "\")" n>
> + "(revision \"" p "\"))")
> diff --git a/etc/snippets/tempel/text-mode.eld
> b/etc/snippets/tempel/text-mode.eld
> new file mode 100644
> index 0000000000..a51a6719d7
> --- /dev/null
> +++ b/etc/snippets/tempel/text-mode.eld
> @@ -0,0 +1,101 @@
> +-*- mode: lisp-data -*-
> +
> +text-mode :when (git-commit-mode)
> +
> +(add
> + "gnu: Add "
> + (p
> +  (with-temp-buffer
> +    (magit-git-wash #'magit-diff-wash-diffs
> +      "diff" "--staged")
> +    (goto-char (point-min))
> +    (when (re-search-forward "\\+(define-public \\(\\S-+\\)" nil
> 'noerror)
> +      (match-string-no-properties 1)))
> +  var ) "." n n
> + "* " (car (magit-staged-files)) " (" (s var ) "): New variable.")
Pro tip: add a space after add (i.e. add\ ) or use add... as the name.
This way, you guard against premature expansion in corfu and other
packages likely to be used in combination with tempel.

> +(remove
> + "gnu: Remove "
> + (p (with-temp-buffer
> +      (magit-git-wash #'magit-diff-wash-diffs
> +        "diff" "--staged")
> +      (goto-char (point-min))
> +      (when (re-search-forward "\\-(define-public \\(\\S-+\\)" nil
> 'noerror)
> +        (match-string-no-properties 1)))
> +    var) "." n n
> + "* " (car (magit-staged-files)) " (" (s var) "): Delete variable.")
> +
> +(rename
> + "gnu: "
> + (p (with-temp-buffer
> +      (magit-git-wash #'magit-diff-wash-diffs
> +        "diff" "--staged")
> +      (beginning-of-buffer)
> +      (when (search-forward "-(define-public " nil 'noerror)
> +        (thing-at-point 'sexp 'no-properties)))
> +    prev-var)
> + ": Rename package to "
> + (p (with-temp-buffer
> +      (magit-git-wash #'magit-diff-wash-diffs
> +        "diff" "--staged")
> +      (beginning-of-buffer)
> +      (when (search-forward "+(define-public " nil 'noerror)
> +        (thing-at-point 'sexp 'no-properties)))
> +    new-var) "." n n
> + "* " (car (magit-staged-files)) " (" (s prev-var) "): Define in
> terms of" n
> + "'deprecated-package'." n
> + "(" (s new-var) "): New variable, formerly known as \"" (s prev-
> var) "\".")
> +
> +(update
> + "gnu: "
> + (p (with-temp-buffer
> +      (magit-git-wash #'magit-diff-wash-diffs
> +        "diff" "--staged")
> +      (goto-char (point-min))
> +      (when (re-search-forward "^[ ]*(define-public \\(\\S-+\\)" nil
> 'noerror)
> +        (match-string-no-properties 1)))
> +    var)
> + ": Update to "
> + (p (with-temp-buffer
> +      (magit-git-wash #'magit-diff-wash-diffs
> +        "diff" "--staged")
> +      (goto-char (point-min))
> +      (search-forward "name" nil 'noerror)
> +      (search-forward "+" nil 'noerror)   ; first change
> +      (when (and (search-forward "version " nil 'noerror)
> +                 (looking-at-p "\""))
> +        (let ((end (save-excursion (search-forward "\")" nil
> 'noerror))))
> +          (when end
> +            (forward-char)
> +            (buffer-substring-no-properties (point) (- end 2))))))
> +    version) "." n n
> + "* " (car (magit-staged-files)) " (" (s var) "): Update to " (s
> version) "."
> + (mapconcat (lambda (file) (concat "* " file)) (cdr (magit-staged-
> files))) n)
> +
> +(addcl
> + "gnu: Add cl-"
> + (p (replace-regexp-in-string
> +     "^cl-" "" (with-temp-buffer
> +                 (magit-git-wash #'magit-diff-wash-diffs
> +                   "diff" "--staged")
> +                 (beginning-of-buffer)
> +                 (when (search-forward "+(define-public " nil
> 'noerror)
> +                   (replace-regexp-in-string
> +                    "^sbcl-" ""
> +                    (thing-at-point 'sexp 'no-properties)))))
> +    var) "." n n
> + "* " (car (magit-staged-files))
> + " (cl-" (s var)  ", ecl-" (s var) ", sbcl-" (s var) "): New
> variables.")
> +
> +(https
> + "gnu: "
> + (p (with-temp-buffer
> +      (magit-git-wash #'magit-diff-wash-diffs
> +        "diff" "--staged")
> +      (goto-char (point-min))
> +      (when (re-search-forward "^[ ]*(define-public \\(\\S-+\\)" nil
> 'noerror)
> +        (match-string-no-properties 1)))
> +    var)
> + ": Use HTTPS home page." n n
> + "* " (car (magit-staged-files)) " (" (s var) ")[home-page]: Use
> HTTPS." n
> + (mapconcat (lambda (file) (concat "* " file)) (cdr (magit-staged-
> files))) n)

Cheers
Nicolas Graves Aug. 12, 2022, 6:20 a.m. UTC | #2
On 2022-08-11 18:21, Liliana Marie Prikler wrote:

> I'm not too sure about the suffix here.  I'd personally prefer a
> suffix-less file with a mode-line.

OK, I thought it was necessary because of a previous failed attempt, but
it seems to work. The tempel path cannot be a directory though, it is
necessary to put a wildcard (at least using this version of TempEL).

>> +text-mode :when (git-commit-mode)

Here I have a bug when magit has never been opened before opening a
text-mode file, saying that git-commit-mode is not defined. Not sure
what the best way to resolve this is. Will investigate that.

> I'd suggest skipping the completing-read and just (p "gnu").  Most
> build systems should be easy enough to type without autocompletion.

But then you have to know the name and loose the listing if you're a
beginner and not sure. I'm fine with both options. I will follow your
suggestion if you confirm that you took this type of user into account
and still prefer just (p "gnu").

> Here, I think (p "url-fetch"), but (s "method") might also work.

I'm not sure I understand what you meant here. Can you write the final
method you would get this way?

> Will this cl-case be dynamically recomputed?  I wonder if we can get
> the result of the previous p/s here...

Yes it is recomputed. p/s stores the variable and for evaluations "Named
fields are lexically bound." It works when tested, we can get the result
of the previous p. As Daniel Mendler stressed here
(https://github.com/minad/tempel/issues/65), there is no possibility to
do a recursive template i.e. with a FORM evaluating to elements of a
snippet. So the best thing we might do with conditionals is to return a
string.

> Rather than that I think adding a template (git-file-name...) which
> expands to (file-name (git-file-name (p "name") (p "version")) and
> variants for the others is a better idea.

In most guix packages, it is left as simply name and version strings,
since they are defined for the package itself. I took the same approah
as the yasnippet template, since these field are almost always left
untouched. I don't see the benefit of this other approach.


Your other comments have been taken into account, I'm sending an updated
series as soon as I'm done with the git-commit-mode issue. Feel free to
send an idea for this issue if you have one! 

--
Best regards,
Nicolas Graves
Liliana Marie Prikler Aug. 30, 2022, 9:51 a.m. UTC | #3
Am Freitag, dem 12.08.2022 um 08:20 +0200 schrieb Nicolas Graves:
> > > +text-mode :when (git-commit-mode)
> 
> Here I have a bug when magit has never been opened before opening a
> text-mode file, saying that git-commit-mode is not defined. Not sure
> what the best way to resolve this is. Will investigate that.
A strange error indeed.  I encounter a similar one if I use emacs as
EDITOR in git.

> > I'd suggest skipping the completing-read and just (p "gnu").  Most
> > build systems should be easy enough to type without autocompletion.
> 
> But then you have to know the name and loose the listing if you're a
> beginner and not sure. I'm fine with both options. I will follow your
> suggestion if you confirm that you took this type of user into
> account and still prefer just (p "gnu").
The reason I prefer (p "gnu") is because completing-read completely
breaks tempel.  Yes, I'm aware that this won't yield complete feature
parity between tempel and yasnippet, but you have to consider that
these *are* distinct systems after all.

FWIW you could try using NOINSERT t for the p, but I'd hazard a guess
that that still won't give you the prompt at the right time with the
correct safeties.

> > Here, I think (p "url-fetch"), but (s "method") might also work.
> 
> I'm not sure I understand what you meant here. Can you write the
> final method you would get this way?
I got (p "url-fetch" method), which uses "url-fetch" as default value
and binds method.

> > Will this cl-case be dynamically recomputed?  I wonder if we can
> > get the result of the previous p/s here...
> 
> Yes it is recomputed. p/s stores the variable and for evaluations
> "Named fields are lexically bound." It works when tested, we can get
> the result of the previous p. As Daniel Mendler stressed here
> (https://github.com/minad/tempel/issues/65), there is no possibility
> to do a recursive template i.e. with a FORM evaluating to elements of
> a snippet. So the best thing we might do with conditionals is to
> return a string.
Bummer, but I got something working out of it anyway.

> > Rather than that I think adding a template (git-file-name...) which
> > expands to (file-name (git-file-name (p "name") (p "version")) and
> > variants for the others is a better idea.
> 
> In most guix packages, it is left as simply name and version strings,
> since they are defined for the package itself. I took the same
> approach as the yasnippet template, since these field are almost
> always left untouched. I don't see the benefit of this other
> approach.
The benefit would have been that auto-completion is relatively cheap,
so folks could just write git, see git-file-name, TAB, M-RET, done.

> Your other comments have been taken into account, I'm sending an
> updated series as soon as I'm done with the git-commit-mode issue.
> Feel free to send an idea for this issue if you have one! 
I see you already solved it, so I'm now processing v3.  As laid out
above, I replaced all occurrences of completing-read with simple ps, as
those are easier to work with in tempel.  I did keep your cl-case to
conditionally insert git-file-name, etc. but I fixed it up a little so
that it's dynamically recomputed.  I also fixed up the documentation
and dropped the license: completion as it was a completing-read once
more.  You could replace those with a bunch of self-expanding snippets,
but I think geiser and/or dabbrev ought to have us covered here.

Cheers
Liliana Marie Prikler Aug. 30, 2022, 9:53 a.m. UTC | #4
Am Dienstag, dem 30.08.2022 um 11:51 +0200 schrieb Liliana Marie
Prikler:
> I see you already solved it, so I'm now processing v3...
Marking as done, since I did push the modified v3

Cheers
diff mbox series

Patch

diff --git a/doc/contributing.texi b/doc/contributing.texi
index 02c7c5ae59..808d71e40a 100644
--- a/doc/contributing.texi
+++ b/doc/contributing.texi
@@ -320,15 +320,22 @@  s-expression, etc.
 @cindex reducing boilerplate
 We also provide templates for common git commit messages and package
 definitions in the @file{etc/snippets} directory.  These templates can
-be used with @url{https://joaotavora.github.io/yasnippet/, YASnippet} to
-expand short trigger strings to interactive text snippets.  You may want
-to add the snippets directory to the @var{yas-snippet-dirs} variable in
-Emacs.
+be used to expand short trigger strings to interactive text snippets,
+with either @url{https://joaotavora.github.io/yasnippet/, YASnippet} or
+@url{https://github.com/minad/tempel/, Tempel}.  You may want to add the
+@file{etc/snippets/yas} (respectively @file{etc/snippets/tempel/*.eld}
+or selected files) snippets directory to the @var{yas-snippet-dirs}
+(@var{tempel-path} resp.) variable in Emacs.
 
 @lisp
 ;; @r{Assuming the Guix checkout is in ~/src/guix.}
+;; @r{Yasnippet}
 (with-eval-after-load 'yasnippet
-  (add-to-list 'yas-snippet-dirs "~/src/guix/etc/snippets"))
+  (add-to-list 'yas-snippet-dirs "~/src/guix/etc/snippets/yas"))
+;;@r{Tempel}
+(with-eval-after-load 'tempel
+         (add-to-list 'tempel-path
+         "~/src/guix/etc/snippets/tempel/*.eld"))
 @end lisp
 
 The commit message snippets depend on @url{https://magit.vc/, Magit} to
diff --git a/etc/snippets/tempel/scheme-mode.eld b/etc/snippets/tempel/scheme-mode.eld
new file mode 100644
index 0000000000..5fcd7030b8
--- /dev/null
+++ b/etc/snippets/tempel/scheme-mode.eld
@@ -0,0 +1,113 @@ 
+-*- mode: lisp-data -*-
+
+scheme-mode
+
+(package...
+ "(define-public " (s name) n>
+ "(package" n>
+ "(name \"" (s name) "\")" n>
+ "(version \"" p "\")" n>
+ "(source origin..." p ")" n>
+ "(build-system \""
+ (p (completing-read "build-system:"
+                     '("android-ndk"
+                       "ant"
+                       "asdf"
+                       "cargo"
+                       "chicken"
+                       "clojure"
+                       "cmake"
+                       "copy"
+                       "dub"
+                       "dune"
+                       "emacs"
+                       "font"
+                       "glib-or-gtk"
+                       "gnu"
+                       "go"
+                       "guile"
+                       "haskell"
+                       "julia"
+                       "linux-module"
+                       "maven"
+                       "meson"
+                       "minetest"
+                       "minify"
+                       "node"
+                       "ocaml"
+                       "perl"
+                       "python"
+                       "qt"
+                       "r"
+                       "rakudo"
+                       "rebar"
+                       "renpy"
+                       "ruby"
+                       "scons"
+                       "texlive"
+                       "trivial"
+                       "waf")
+                     nil t) "-build-system")
+ "\")" n>
+ "(home-page \"" p "\")" n>
+ "(synopsis \"" p "\")" n>
+ "(description \"" p "\")" n>
+ "(license " p ")))" n)
+
+(origin...
+ "(origin" n>
+ "(method \""
+ (p (completing-read "method:"
+                     '("url-fetch"
+                       "url-fetch/tarbomb"
+                       "url-fetch/zipbomb"
+                       "cvs-fetch"
+                       "git-fetch"
+                       "hg-fetch"
+                       "svn-fetch"
+                       "bzr-fetch")
+                     nil t) method) "\")" n>
+ "(uri " (cl-case (intern method)
+           ('git-fetch "git-reference...")
+           ('svn-fetch "svn-reference...")
+           ('hg-fetch  "hg-reference...")
+           ('cvs-fetch "cvs-reference...")
+           ('bzr-fetch "bzr-reference...")
+           (t          "(string-append \"https://"))
+ p
+ (if (equal (substring method 0 9)  "url-fetch") "\")")
+ ")" n>
+ (cl-case (intern method)
+   ('git-fetch "(file-name (git-file-name name version))\n")
+   ('hg-fetch "(file-name (hg-file-name name version))\n")
+   ('svn-fetch "(file-name (string-append name \"-\" version \"-checkout\"))\n")
+   ('cvs-fetch "(file-name (string-append name \"-\" version \"-checkout\"))\n")
+   ('bzr-fetch "(file-name (string-append name \"-\" version \"-checkout\"))\n")
+   (t          "")) >
+ "(sha256" n> "(base32 \"" p "\")))")
+
+(git-reference...
+ "(git-reference" n>
+ "(url \"" p "\")" n>
+ "(commit \"" p "\"))")
+
+(svn-reference...
+ "(svn-reference" n>
+ "(url \"" p "\")" n>
+ "(revision \"" p "\"))")
+
+(cvs-reference...
+ "(cvs-reference" n>
+ "(root-directory \"" p "\")" n>
+ "(module \"" p "\")" n>
+ "(revision \"" p "\"))")
+
+(hg-reference...
+ "(hg-reference" n>
+ "(url \"" p "\")" n>
+ "(changeset \"" p "\"))")
+
+(bzr-reference...
+ "(bzr-reference" n>
+ "(url \"" p "\")" n>
+ "(revision \"" p "\"))")
diff --git a/etc/snippets/tempel/text-mode.eld b/etc/snippets/tempel/text-mode.eld
new file mode 100644
index 0000000000..a51a6719d7
--- /dev/null
+++ b/etc/snippets/tempel/text-mode.eld
@@ -0,0 +1,101 @@ 
+-*- mode: lisp-data -*-
+
+text-mode :when (git-commit-mode)
+
+(add
+ "gnu: Add "
+ (p
+  (with-temp-buffer
+    (magit-git-wash #'magit-diff-wash-diffs
+      "diff" "--staged")
+    (goto-char (point-min))
+    (when (re-search-forward "\\+(define-public \\(\\S-+\\)" nil 'noerror)
+      (match-string-no-properties 1)))
+  var ) "." n n
+ "* " (car (magit-staged-files)) " (" (s var ) "): New variable.")
+
+(remove
+ "gnu: Remove "
+ (p (with-temp-buffer
+      (magit-git-wash #'magit-diff-wash-diffs
+        "diff" "--staged")
+      (goto-char (point-min))
+      (when (re-search-forward "\\-(define-public \\(\\S-+\\)" nil 'noerror)
+        (match-string-no-properties 1)))
+    var) "." n n
+ "* " (car (magit-staged-files)) " (" (s var) "): Delete variable.")
+
+(rename
+ "gnu: "
+ (p (with-temp-buffer
+      (magit-git-wash #'magit-diff-wash-diffs
+        "diff" "--staged")
+      (beginning-of-buffer)
+      (when (search-forward "-(define-public " nil 'noerror)
+        (thing-at-point 'sexp 'no-properties)))
+    prev-var)
+ ": Rename package to "
+ (p (with-temp-buffer
+      (magit-git-wash #'magit-diff-wash-diffs
+        "diff" "--staged")
+      (beginning-of-buffer)
+      (when (search-forward "+(define-public " nil 'noerror)
+        (thing-at-point 'sexp 'no-properties)))
+    new-var) "." n n
+ "* " (car (magit-staged-files)) " (" (s prev-var) "): Define in terms of" n
+ "'deprecated-package'." n
+ "(" (s new-var) "): New variable, formerly known as \"" (s prev-var) "\".")
+
+(update
+ "gnu: "
+ (p (with-temp-buffer
+      (magit-git-wash #'magit-diff-wash-diffs
+        "diff" "--staged")
+      (goto-char (point-min))
+      (when (re-search-forward "^[ ]*(define-public \\(\\S-+\\)" nil 'noerror)
+        (match-string-no-properties 1)))
+    var)
+ ": Update to "
+ (p (with-temp-buffer
+      (magit-git-wash #'magit-diff-wash-diffs
+        "diff" "--staged")
+      (goto-char (point-min))
+      (search-forward "name" nil 'noerror)
+      (search-forward "+" nil 'noerror)   ; first change
+      (when (and (search-forward "version " nil 'noerror)
+                 (looking-at-p "\""))
+        (let ((end (save-excursion (search-forward "\")" nil 'noerror))))
+          (when end
+            (forward-char)
+            (buffer-substring-no-properties (point) (- end 2))))))
+    version) "." n n
+ "* " (car (magit-staged-files)) " (" (s var) "): Update to " (s version) "."
+ (mapconcat (lambda (file) (concat "* " file)) (cdr (magit-staged-files))) n)
+
+(addcl
+ "gnu: Add cl-"
+ (p (replace-regexp-in-string
+     "^cl-" "" (with-temp-buffer
+                 (magit-git-wash #'magit-diff-wash-diffs
+                   "diff" "--staged")
+                 (beginning-of-buffer)
+                 (when (search-forward "+(define-public " nil 'noerror)
+                   (replace-regexp-in-string
+                    "^sbcl-" ""
+                    (thing-at-point 'sexp 'no-properties)))))
+    var) "." n n
+ "* " (car (magit-staged-files))
+ " (cl-" (s var)  ", ecl-" (s var) ", sbcl-" (s var) "): New variables.")
+
+(https
+ "gnu: "
+ (p (with-temp-buffer
+      (magit-git-wash #'magit-diff-wash-diffs
+        "diff" "--staged")
+      (goto-char (point-min))
+      (when (re-search-forward "^[ ]*(define-public \\(\\S-+\\)" nil 'noerror)
+        (match-string-no-properties 1)))
+    var)
+ ": Use HTTPS home page." n n
+ "* " (car (magit-staged-files)) " (" (s var) ")[home-page]: Use HTTPS." n
+ (mapconcat (lambda (file) (concat "* " file)) (cdr (magit-staged-files))) n)
diff --git a/etc/snippets/scheme-mode/guix-bzr-reference b/etc/snippets/yas/scheme-mode/guix-bzr-reference
similarity index 100%
rename from etc/snippets/scheme-mode/guix-bzr-reference
rename to etc/snippets/yas/scheme-mode/guix-bzr-reference
diff --git a/etc/snippets/scheme-mode/guix-cvs-reference b/etc/snippets/yas/scheme-mode/guix-cvs-reference
similarity index 100%
rename from etc/snippets/scheme-mode/guix-cvs-reference
rename to etc/snippets/yas/scheme-mode/guix-cvs-reference
diff --git a/etc/snippets/scheme-mode/guix-git-reference b/etc/snippets/yas/scheme-mode/guix-git-reference
similarity index 100%
rename from etc/snippets/scheme-mode/guix-git-reference
rename to etc/snippets/yas/scheme-mode/guix-git-reference
diff --git a/etc/snippets/scheme-mode/guix-hg-reference b/etc/snippets/yas/scheme-mode/guix-hg-reference
similarity index 100%
rename from etc/snippets/scheme-mode/guix-hg-reference
rename to etc/snippets/yas/scheme-mode/guix-hg-reference
diff --git a/etc/snippets/scheme-mode/guix-origin b/etc/snippets/yas/scheme-mode/guix-origin
similarity index 100%
rename from etc/snippets/scheme-mode/guix-origin
rename to etc/snippets/yas/scheme-mode/guix-origin
diff --git a/etc/snippets/scheme-mode/guix-package b/etc/snippets/yas/scheme-mode/guix-package
similarity index 100%
rename from etc/snippets/scheme-mode/guix-package
rename to etc/snippets/yas/scheme-mode/guix-package
diff --git a/etc/snippets/scheme-mode/guix-svn-reference b/etc/snippets/yas/scheme-mode/guix-svn-reference
similarity index 100%
rename from etc/snippets/scheme-mode/guix-svn-reference
rename to etc/snippets/yas/scheme-mode/guix-svn-reference
diff --git a/etc/snippets/text-mode/guix-commit-message-add-cl-package b/etc/snippets/yas/text-mode/guix-commit-message-add-cl-package
similarity index 100%
rename from etc/snippets/text-mode/guix-commit-message-add-cl-package
rename to etc/snippets/yas/text-mode/guix-commit-message-add-cl-package
diff --git a/etc/snippets/text-mode/guix-commit-message-add-package b/etc/snippets/yas/text-mode/guix-commit-message-add-package
similarity index 100%
rename from etc/snippets/text-mode/guix-commit-message-add-package
rename to etc/snippets/yas/text-mode/guix-commit-message-add-package
diff --git a/etc/snippets/text-mode/guix-commit-message-remove-package b/etc/snippets/yas/text-mode/guix-commit-message-remove-package
similarity index 100%
rename from etc/snippets/text-mode/guix-commit-message-remove-package
rename to etc/snippets/yas/text-mode/guix-commit-message-remove-package
diff --git a/etc/snippets/text-mode/guix-commit-message-rename-package b/etc/snippets/yas/text-mode/guix-commit-message-rename-package
similarity index 100%
rename from etc/snippets/text-mode/guix-commit-message-rename-package
rename to etc/snippets/yas/text-mode/guix-commit-message-rename-package
diff --git a/etc/snippets/text-mode/guix-commit-message-update-package b/etc/snippets/yas/text-mode/guix-commit-message-update-package
similarity index 100%
rename from etc/snippets/text-mode/guix-commit-message-update-package
rename to etc/snippets/yas/text-mode/guix-commit-message-update-package
diff --git a/etc/snippets/text-mode/guix-commit-message-use-https-home-page b/etc/snippets/yas/text-mode/guix-commit-message-use-https-home-page
similarity index 100%
rename from etc/snippets/text-mode/guix-commit-message-use-https-home-page
rename to etc/snippets/yas/text-mode/guix-commit-message-use-https-home-page