joaotavora/yasnippet

Suggestion for a repeat snippet feature.

MirkoHernandez opened this issue · 8 comments

I'm currently using the following method to repeat snippets: a yas-after-exit-snippet-hook that uses yas-lookup-snippet to find the current snippet and expand it. To exit the snippet I use the prefix argument C-u . Maybe something like this could be implemented in yasnippet with better syntax (a #repeat field).

# -*- mode: snippet -*-
# name:parameters 
# key: p 
# type: command
# --
(yas-expand-snippet "$1 ,"  
nil nil
'((yas-after-exit-snippet-hook
 (lambda ()
	 (if (not current-prefix-arg)
              (yas-expand-snippet (yas-lookup-snippet  "parameters)
	  (search-backward-regexp ",")
	 (delete-char 1)
	(fixup-whitespace))))))

Could you clarify what "better syntax" you're thinking of?

Just a repeat directive. Maybe a repeat hook (or just a function) could be implemented to run before yas-after-exit-snippet-hook, repeating the snippet by name a number of times or indefinitely until the prefix argument is used.

# -*- mode: snippet -*-
# name:parameters 
# key: p 
# repeat: t (or a number)
# --
"$1, "

The left-over comma could be handled with the regular yas-after-exit-snippet-hook.

The example is just a basic demonstration of the functionality, a more practical one would be a bunch of json fields. Other examples: css rules, html tags like list items, case statements. It is easier to just type without having to write and expand each snippet.

One could have a regular snippet and the repeatable version and use the more convenient one for each use case.

# -*- mode: snippet -*-
# name:json-fields 
# key: jf
# repeat: t (or a number)
# --
"$1 ":  $2, 

That's not very satisfactory.

Yes I agree, but using yas-after-exit-snippet-hook seems like the right thing to do (cleanup work after exiting the snippet), maybe there is another way I'm not familiar with.
Some alternatives: maybe a directive for yas-after-exit-snippet-hook , or maybe a different hook that runs on a per snippet basis.

This suggests that instead of having the "repeat" be a property of the
template, maybe we should have a command to repeat a(ny) template.

This seems like a better solution than the initial proposal.

I looked for a "last snippet" variable in the yanippet code and could not find it. I think a possible implementation would be just adding it.

(defvar yas-last-snippet nil)
(make-variable-buffer-local 'yas-last-snippet)

(defun yas-expand-snippet (snippet &optional start end expand-env)
;;; omited code
    (let ((content (if (yas--template-p snippet)
                       (yas--template-content snippet)
                     snippet)))
      (setq yas-last-snippet snippet)
)
(defun my/expand-last-snippet ()
  (interactive)
  (yas-expand-snippet yas-last-snippet))

[ You can use defvar-local instead. ]

Thanks I was not aware of this.

But for the user that requires hitting a key for each repetition,
whereas your current approach requires hitting a key only for the
last repetition.

I don't think that this would be a problem for most snippets. It is probably a bit annoying for simple snippets with one or two fields but would otherwise work fine.

How do you start your repeated template?

In my approach I just use tab (or whatever yas-next-field is), using tab in the last field repeats the snippet.
The problem is how to stop repeating the snippet, I use the prefix argument C-u which is kind of cumbersome, I tried yas-abort-snippet but it does not work, I believe it uses the exit hook.

Maybe we can provide a new command yas-expand-repeatedly?

Great!, I think this probably the best solution, and it would allow snippets already created to be repeatable.
I still think the previous expand-last command is useful for a different use case.