Insert yasnippet by name - emacs

I want to insert a specific yasnippet as part of a function in emacs-lisp. Is there a way to do that?
The only command that seems related is yas/insert-snippet, but it simply opens a popup with all the options and the documentation doesn't say anything about bypassing the popup by specifing the snippet name.

yas/insert-snippet is indeed just a thin wrapper around yas/expand-snippet for interactive use. However, the internal structures are... interesting. Judging from the source code the following does work for me when I want to expand the "defun" snippet in elisp-mode:
(yas/expand-snippet
(yas/template-content (cdar (mapcan #'(lambda (table)
(yas/fetch table "defun"))
(yas/get-snippet-tables)))))

As the author of yasnippet, I think you'd rather not rely on internal details of yasnippet's interesting data structures, which may change in the future. I would do this based on the documentation of yas/insert-snippet and yas/prompt-functions:
(defun yas/insert-by-name (name)
(flet ((dummy-prompt
(prompt choices &optional display-fn)
(declare (ignore prompt))
(or (find name choices :key display-fn :test #'string=)
(throw 'notfound nil))))
(let ((yas/prompt-functions '(dummy-prompt)))
(catch 'notfound
(yas/insert-snippet t)))))
(yas/insert-by-name "defun")

I'm just getting into yasnippet and I wanted to automatically insert one of my snippets upon opening a new file for certain modes. That led me to here but I've generated a slightly different solution. Providing yet another alternative: ("new-shell" is the name of my personal snippet for providing a new shell script template)
(defun jsm/new-file-snippet (key)
"Call particular yasnippet template for newly created
files. Use by adding a lambda function to the particular mode
hook passing the correct yasnippet key"
(interactive)
(if (= (buffer-size) 0)
(progn
(insert key)
(call-interactively 'yas-expand))))
(add-hook 'sh-mode-hook '(lambda () (jsm/new-file-snippet "new-shell")))
IMO, my solution is a tad less susceptible to breaking should yasnippet change dramatically.

This is 2022 now, so we can simply do the following :
(yas-expand-snippet (yas-lookup-snippet "name-of-your-snippet"))
See the documentation

Related

Open Org Capture buffer in specific window?

I've been an Emacs user for about a year or so. I routinely have the same window set up each session (four windows).
I've set up capture templates and can capture what I want, but: instead of capture mode temporarily jerking me out of my window setup, I'd like the chosen capture template to open in a new (fifth) window, preserving my existing layout. I typically want the capture template open for a while, so it's disruptive.
This seems like it would be an obvious option, but I can't figure it out. Thanks in advance to all the Emacs heads out there.
I came up with a easier-to-use version of Dan's answer to the linked question:
(defun my-org-capture-place-template-dont-delete-windows (oldfun &rest args)
(cl-letf (((symbol-function 'delete-other-windows) 'ignore))
(apply oldfun args)))
(with-eval-after-load "org-capture"
(advice-add 'org-capture-place-template :around 'my-org-capture-place-template-dont-delete-windows))
That is, instead of having to modify Org-mode code and remove the call to delete-other-windows, this piece of code temporarily redefines delete-other-windows to ignore while org-capture-place-template is being called.
It doesn't do quite what you want: it picks one of the existing windows and puts the capture buffer there. At least it's better than the default behaviour of removing all previous windows but one.
There's probably a way to do what you want by customising the variable display-buffer-alist, but I couldn't figure it out...
You could also use https://github.com/raxod502/el-patch and patch org-capture after loading (look for the (el-patch-remove (delete-other-windows))):
(el-patch-feature org-capture)
(with-eval-after-load 'org-capture
(el-patch-defun org-capture-place-template (&optional inhibit-wconf-store)
"Insert the template at the target location, and display the buffer.
When `inhibit-wconf-store', don't store the window configuration, as it
may have been stored before."
(unless inhibit-wconf-store
(org-capture-put :return-to-wconf (current-window-configuration)))
(el-patch-remove (delete-other-windows))
(org-switch-to-buffer-other-window
(org-capture-get-indirect-buffer (org-capture-get :buffer) "CAPTURE"))
(widen)
(org-show-all)
(goto-char (org-capture-get :pos))
(setq-local outline-level 'org-outline-level)
(pcase (org-capture-get :type)
((or `nil `entry) (org-capture-place-entry))
(`table-line (org-capture-place-table-line))
(`plain (org-capture-place-plain-text))
(`item (org-capture-place-item))
(`checkitem (org-capture-place-item)))
(org-capture-mode 1)
(setq-local org-capture-current-plist org-capture-plist)) )
For some reason, the #legoscia approach fails for me in emacs 28.
So here is the el-patch snippet as suggested previously:
(el-patch-feature org-capture)
(with-eval-after-load 'org-capture
(el-patch-define-and-eval-template
(defun org-capture-place-template)
(el-patch-remove (delete-other-windows))))

How to never expand yasnippets in comments and strings

I'd like to disable YASnippet expansion (for example, if) in comments and strings, but don't find how to do that in a generic way.
On The condition system, they say how to do it for Python, but I'd like to get it working for all prog-modes at once, and I'm not aware of any function which tests "in string/comment", independently of the language.
Is there still a way to do so?
Using lawlist's suggestion and adding it to prog-mode-hook:
(defun yas-no-expand-in-comment/string ()
(setq yas-buffer-local-condition
'(if (nth 8 (syntax-ppss)) ;; non-nil if in a string or comment
'(require-snippet-condition . force-in-comment)
t)))
(add-hook 'prog-mode-hook 'yas-no-expand-in-comment/string)

Sublime Text 2's "Goto Anything" (or instant search) for Emacs?

I tried out Sublime Text 2 recently, and I found Goto Anything superbly useful for navigating source code (Ctrl-P file#symbol seems to work really well). Is there something similar for Emacs? Preferably something that just works, without a ton of custom elisp.
What I've tried so far:
I've seen Helm and Anything, but as far as I understand neither of them is capable of actual "instant" search (see edit below).
I've used multi-occur-in-matching-buffers, but it too seems unable to satisfy the "instant" criterion.
imenu / idomenu works well for single files, but doesn't work across files.
I currently use #2 and #3 together, as a poor substitute for Goto Anything.
If not an exact clone of Goto Anything, then I could make do with a naive instant search solution (one that searches for a given string across all open buffers and displays results dynamically). So that's acceptable too.
I use Emacs 24.2, so any v24-only elisp is also fine.
EDIT: I gave Helm another shot, at event_jr's suggestion, and I found that it does support instant searching across all open buffers. helm-multi-occur + helm-follow-mode comes surprisingly close to meeting my needs, the only minor issues being (at the risk of sounding nit-picky):
I haven't found a way to turn on helm-follow-mode automatically when I run helm-multi-occur. I have to invoke it manually with C-c C-f. Anyone care to take a shot at this with a snippet of elisp? (see edit #2 below)
it isn't "intelligent" like ST2's Goto Anything (i.e., it doesn't understand "symbols" in source code, like Goto Anything does).
EDIT #2: Now I've got most of Goto Anything, thanks to event_jr's answer below (and of course, thanks to Helm's creator, Thierry Volpiatto). I recommend it heartily to anyone looking for a similar feature. Below is the elisp I'm currently using:
;; instant recursive grep on a directory with helm
(defun instant-rgrep-using-helm ()
"Recursive grep in a directory."
(interactive)
(let ((helm-after-initialize-hook #'helm-follow-mode))
(helm-do-grep)))
;; instant search across all buffers with helm
(defun instant-search-using-helm ()
"Multi-occur in all buffers backed by files."
(interactive)
(let ((helm-after-initialize-hook #'helm-follow-mode))
(helm-multi-occur
(delq nil
(mapcar (lambda (b)
(when (buffer-file-name b) (buffer-name b)))
(buffer-list))))))
;; set keybindings
(global-set-key (kbd "C-M-s") 'instant-search-using-helm)
(global-set-key (kbd "C-M-S-s") 'helm-resume)
(global-set-key (kbd "C-M-g") 'instant-rgrep-using-helm)
Just use helm.
It is perhaps more configuration than you asked for, but once you get it
configured how you like, it should be quite comfortable. Very much like Emacs
;).
And you should file a bug with Thierry for getting some more newbie friendly
defaults. He is quite responsive with issues.
helm-multi-occur
Primarily multi-buffer interactive "occur" is provided through
helm-multi-occur. If you execute the command, you'll notice that you have
to pick some buffers first (use C-SPC to select from the list,
M-SPC to select all). Then you can enter your query at the next
prompt. It's easy to make your own version that skips the buffer selection
like so:
(eval-after-load "helm-regexp"
'(setq helm-source-moccur
(helm-make-source "Moccur"
'helm-source-multi-occur :follow 1)))
(defun my-helm-multi-all ()
"multi-occur in all buffers backed by files."
(interactive)
(helm-multi-occur
(delq nil
(mapcar (lambda (b)
(when (buffer-file-name b) (buffer-name b)))
(buffer-list)))))
helm-buffers-list
Often you don't care about the exact occurrences of the query string, but want a
list of all buffers that contain it.
helm-buffers-list has some tricks up its sleeve. The first symbol you
specify is filtering by major-mode, and you can use the "#" prefix to narrow
the list to buffers that contain a string.
To wit, "ruby #prompt" will show you a list of buffers whose major-mode
contains "ruby" and whose contents contains "prompt". Or you can just use "#prompt" to show all buffers that contain "prompt".
Powerful and comfortable once you get used to it.
EDIT modified my-helm-multi-all to enable helm-follow-mode.
EDIT 2 update helm-follow-mode code to reflect helm changes.
EDIT 3 updated again to reflect helm changes
Emacs has Projectile satisfy your need:
jump to a file in project
multi-occur in project buffers
Heml is far from the fuzzy searching of ST3.
Fiplr looks promising but doesn't work on my laptop (see first issue on the github)
Simp.el looks like Fiplr but doesn't work either on my end.
Projectile works for me! Here's your solution!
I used also ido-mode and flx-ido for the fuzzy searching,
and for the vertical way of displaying results I use this in my .emacs:
;; Display ido results vertically, rather than horizontally
(setq ido-decorations (quote ("\n-> " "" "\n " "\n ..." "[" "]" " [No match]" " [Matched]" " [Not readable]" " [Too big]" " [Confirm]")))
(defun ido-disable-line-truncation () (set (make-local-variable 'truncate-lines) nil))
(add-hook 'ido-minibuffer-setup-hook 'ido-disable-line-truncation)
(defun ido-define-keys () ;; C-n/p is more intuitive in vertical layout
(define-key ido-completion-map (kbd "C-n") 'ido-next-match)
(define-key ido-completion-map (kbd "C-p") 'ido-prev-match))
(add-hook 'ido-setup-hook 'ido-define-keys)
Icicles offers some features that are similar to what it seems you are looking for.
C-x b and C-x C-f, to choose buffers or files, allow multi-completion: you can type a pattern to match the buffer/file name and/or a pattern to match content in the buffer/file. Candidates are filtered incrementally as you type (what you call "instant" is what Emacs calls "incremental"). You can refine either or both search patterns progressively, narrowing the choices in different ways. You can visit any number of buffers/files that match, at the same time. You can also use the same method to search the marked files in Dired: C-F.
C-c `(icicle-search) incrementally searches across multiple buffers or files. Again, progressive refinement etc.
The main difference between #1 and #2 is this:
For #1, you just want to find matching buffers or files. You don't care immediately about finding particular occurrences --- any match suffices.
For #2, you provide the buffers or files to search, and you want to navigate among search hits.
You can also use #1 to locate the buffers and files you want, then search their contents: The content-matching pattern you last used is available as the search pattern for Isearch (C-s).
for emacs I customize and modify this solution (for use install helm):
(defun helm-occur-from-point (initial-value)
"Invoke `helm-occur' from point."
(interactive)
(let ((input initial-value)
(bufs (list (buffer-name (current-buffer)))))
;; (isearch-exit)
(helm-occur-init-source)
(helm-attrset 'moccur-buffers bufs helm-source-occur)
(helm-set-local-variable 'helm-multi-occur-buffer-list bufs)
(helm-set-local-variable
'helm-multi-occur-buffer-tick
(cl-loop for b in bufs
collect (buffer-chars-modified-tick (get-buffer b))))
(helm :sources 'helm-source-occur
:buffer "*helm occur*"
:history 'helm-grep-history
:input input
:truncate-lines t)))
(defun get-point-text ()
"Get 'interesting' text at point; either word, or region"
(if mark-active
(buffer-substring (mark) (point))
(thing-at-point 'symbol)))
(defun helm-occur-1 (initial-value)
"Preconfigured helm for Occur with initial input."
(helm-occur-from-point initial-value))
(defun bk-helm-occur ()
"Invoke helm-occur with initial input configured from text at point"
(interactive)
(helm-occur-1 (get-point-text)))
(global-set-key (kbd "M-s-o") 'bk-helm-occur)
primary it based on
#see https://news.ycombinator.com/item?id=6872508 but on last helm versions not work but fixed with my changes (just copy/paste from some internal helm modules)

How can I apply a hook to multiple Emacs modes at once?

I was reading an article about well-formatted Git commits, and I was wondering how I could apply some of the rules to the Magit log mode.
It seems to use 3 major modes simultaneously: Magit, Log, Edit.
So how would I get just those modes, when used together, to hard-wrap at 72 characters automatically?
In answer to the original stated question, if you have a single function to add to numerous hook variables, you could do it like this:
(defun my-add-to-multiple-hooks (function hooks)
(mapc (lambda (hook)
(add-hook hook function))
hooks))
(defun my-turn-on-auto-fill ()
(setq fill-column 72)
(turn-on-auto-fill))
(my-add-to-multiple-hooks
'my-turn-on-auto-fill
'(text-mode-hook
magit-log-edit-mode-hook
change-log-mode-hook))
Not the best example, perhaps, but I have something similar for some common behaviours I want enabled in programming modes, of which there are a great many more to list.
Emacs modes have "base modes" which is to say bade modes. For example python-mode extends prog-mode which itself extends fundamental-mode. All modes extend fundamental-mode. So to hook python-mode plus c-mode but not text-mode, you could hook prog-mode.
There can be only one major mode in Emacs buffer (unless you are using something like MMM or MuMaMo). In your case that one major mode is magit-log-edit-mode, whose name consists of three words ("Magit Log Edit"). You can just add to it whatever hook you like:
(defun my-turn-on-auto-fill ()
(setq fill-column 72)
(turn-on-auto-fill))
(add-hook 'magit-log-edit-mode-hook 'my-turn-on-auto-fill)
In general, you could define your own function, say my-common-hook and add it to all the major modes, for example:
(defun my-common-hook ()
... do stuff ...
)
(add-hook 'one-mode-hook 'my-common-hook)
(add-hook 'another-mode-hook 'my-common-hook)
(add-hook 'a-third-mode-hook 'my-common-hook)
Just seeing this now, but here's what I've done. The end result is that i want to do the following:
(hook-up-modes my-lisps 'standard-lisp-environment).
To do this, i define the following defvars.
(defvar my-lisps "clojure lisp emacs-lisp cider-repl")
(defun standard-lisp-environment ()
(paredit-mode 1)
(rainbow-delimiters-mode 1)
(eldoc-mode 1))
I want to have lisp append -mode-hook to the lisps i use so i have the following:
(defun append-suffix (suffix phrases)
"take SUFFIX and append it to each of the PHRASES."
(mapcar #'(lambda (phrase) (concat phrase suffix)) phrases))
so that ("clojure" "lisp") => ("clojure-mode-hook" "lisp-mode-hook").
Now that we could easily have these, we need their reader symbols, which we easily get from
(defun symbols-from-strings (strings)
"Given a list of strings, get their symbol values"
(mapcar #'intern strings))
And then finally we have the similar form posted above:
(defun multiple-mode-add-hook (modes hook)
"Given a list of x-mode-hook symbols in MODE, add the HOOK to them."
(mapc (lambda (mode) (add-hook mode hook)) modes))
These all operate on the type that makes sense for them, list of strings, list of symbols 'blah-mode-hook, etc. So now we need a nice user facing function that we can work with.
(defun hook-up-modes (strings hook)
(let ((modes (symbols-from-strings
(append-suffix "-mode-hook" (split-string strings)))))
(multiple-mode-add-hook modes hook)))
Now this should be pretty legible: We create our modes from a space delimited list of strings and apply the hook to it. Also, since I've defined a standard-lisp-environment, all my lisps behave similarly, and I can easily remove the hook later if I like. Then the code that actually does work is the super simple phrase
(hook-up-modes my-lisps 'standard-lisp-environment).
(dolist (mode-hook '(org-mode-hook
term-mode-hook))
(add-hook mode-hook (lambda () (display-line-numbers-mode 0))))

Emacs: highlighting TODO *only* in comments

This question is related to another one, Emacs :TODO indicator at left side. I recently came across a minor mode I like a lot called FixmeMode. It supports auto highlighting of TODO marks, and navigating between them. However, I think it makes more sense to recognize the "TODO" strings only in comments, rather than polluting the whole file. Is it possible?
Check out the library fic-mode.el, it has been verified in C++ and Emacs-Lisp.
It was written specifically to answer this question.
The installation is like any standard package:
(require 'fic-mode)
(add-hook 'c++-mode-hook 'turn-on-fic-mode)
Though Wei Hu did ask for an easy way to add it to multiple modes, so here goes:
(defun add-something-to-mode-hooks (mode-list something)
"helper function to add a callback to multiple hooks"
(dolist (mode mode-list)
(add-hook (intern (concat (symbol-name mode) "-mode-hook")) something)))
(add-something-to-mode-hooks '(c++ tcl emacs-lisp) 'turn-on-fic-mode)
It's possible but quite a bit trickier. Fixme mode uses font-lock to do its highlighting, so it works on an as-you-type basis to highlight the keywords. Font-lock hooks in at a very low level, basically running after every change is made to the buffer's contents. It is highly optimized, though, which allows it to appear instantaneous on modern computers.
The TODO indicator in the left fringe is static. Execute the function and all current TODO's are highlighted; change the buffer (adding or removing TODO's) does not change the fringe indicator; that's only changed when the function runs again.
Your approach would have to get into syntax tables, determining first when you're in a comment and then looking for the keywords. The tricky part comes in doing this interactively (i.e. as you type). You should be able to hook into the font-lock constructs to do this, but the function you provide to search for the comment syntax table and then for the keywords better be very efficient, as it will be run each and every time a buffer changes (though it will only run on the changed region, I think). You would want to stuff all of this in font-lock-syntactic-keywords rather than font-lock-keywords because the syntactic-keyword pass happens before the syntactic pass (which happens before the keyword pass), and you need to set TODO inside comments before comments themselves are set.
Sorry it's not a full working-code answer.....
Maybe this will help: there's a fn c-in-literal in
cc-mode, and a similar csharp-in-literal in csharp mode. The
return value is c if in a C-style comment, c++ if in a C++
style comment. You could add that to the code at
Emacs :TODO indicator at left side
to get what you want.
(defun annotate-todo ()
"put fringe marker on TODO: lines in the curent buffer"
(interactive)
(let (lit)
(save-excursion
(goto-char (point-min))
(while (re-search-forward "TODO:" nil t)
(progn
(setq lit (c-in-literal)) ;; or csharp-in-literal
(if (or (eq lit 'c) (eq lit 'c++))
(let ((overlay (make-overlay (- (point) 5) (point))))
(overlay-put overlay 'before-string
(propertize "A"
'display
'(left-fringe ;; right
horizontal-bar
better-fringes-important-bitmap))))))))))
https://github.com/tarsius/hl-todo seems to do exactly what you want. I just tried it and love it.