2

I used (advice-add 'yas-new-snippet :override 'my/yas-new-snippet ) but I was wondering if there was something like this which would allow me to override a function in a function only in that function:

(override-func 'yas-new-snippet 'switch-to-buffer 'switch-to-buffer-other-window)

What I did was override the function to open the snippet in another window. This works but assume that the package maintainer is actively maintaining. I would have to copy this when/if it gets updated. Fortunately for me yas does not get updates frequently. Another thing is that it adds more code to my config.

(defun my/yas-new-snippet (&optional no-template)
  "Pops a new buffer for writing a snippet.

Expands a snippet-writing snippet, unless the optional prefix arg
NO-TEMPLATE is non-nil."
  (interactive "P")
  (let ((guessed-directories (yas--guess-snippet-directories))
        (yas-selected-text (or yas-selected-text
                               (and (region-active-p)
                                    (buffer-substring-no-properties
                                     (region-beginning) (region-end))))))

    ;;(switch-to-buffer yas-new-snippet-buffer-name)
    (switch-to-buffer-other-window yas-new-snippet-buffer-name)
    (erase-buffer)
    (kill-all-local-variables)
    (snippet-mode)
    (yas-minor-mode 1)
    (set (make-local-variable 'yas--guessed-modes)
         (mapcar (lambda (d) (yas--table-mode (car d)))
                 guessed-directories))
    (set (make-local-variable 'default-directory)
         (car (cdr (car guessed-directories))))
    (if (and (not no-template) yas-new-snippet-default)
        (yas-expand-snippet yas-new-snippet-default))))

;; Open new snippet in other window.
(advice-add 'yas-new-snippet :override 'my/yas-new-snippet )


2
  • 1
    How about cl-letf? E.g., (cl-letf (((symbol-function 'yes-or-no-p) #'my-y-or-n-p)) ...)
    – lawlist
    Commented Mar 10, 2023 at 18:03
  • @lawlist Isn't that the same thing that I did with (advice-add 'yas-new-snippet :override 'my/yas-new-snippet ) ? I was wondering if I could override switch-to-buffer inside yas-new-snippet without going inside of the code if that makes sense. Override it from the outside.
    – ritchie
    Commented Mar 10, 2023 at 18:36

1 Answer 1

3

Expanding @lawlist's comment above, here's how to do it with cl-letf.

Let's start with a simple example: you have a function foo which calls a function bar with an argument (foo is the analog of the yas-new-snippet function in your case and bar is the analog of switch-to-buffer) and I want to change foo to call a different function baz instead (the analog of switch-to-buffer-other-window in your case), but from the "outside". So here are the three functions:

  (defun foo ()
    (bar "foo"))

  (defun bar (x)
    (message (format "%s:%s" "bar" x)))

  (defun baz (x)
    (message (format "%s:%s" "baz" x)))

  (foo) ---> "bar:foo"

Define a new function that (effectively) calls baz instead of bar - that's the analog of your my/yas-new-snippet function:

  (defun my/foo ()
    (cl-letf (((symbol-function 'bar) #'baz))
      (foo)))

Now when I call my-foo, it calls baz instead of bar:

(my-foo) ----> "baz:foo"

Note that we are not touching foo at all. We are just temporarily modifying the function cell of the bar symbol to hold the baz function instead. We then call foo which thinks it's calling bar, but we have pulled a switcheroo and it's (effectively) calling baz instead. And then we are out of scope of cl-letf and everything is back the way it was.

So in your case, you can do this:

(defun my/yas-new-snippet (&optional no-template)
  (interactive "P")
  (cl-letf (((symbol-function 'switch-to-buffer) #'switch-to-buffer-other-window))
     (yas-new-snippet no-template)))

I haven't actually tested this (I did test the toy example above), so I hope this works but I'm reasonably sure that it will. EDIT: As the OP points out in a comment, in order for the new function to be compatible with the old function, not only does it have to have the same signature, but it also has to have the same interactive specification, so that the optional argument is handled the same way.

Since we are using the old yas-new-snippet function inside the new function, you should not override the old function with the new one: that would cause an infinite descent. The new function calls the old functions so you need both functions to stick around in Emacs's memory. Instead of the overriding advice, just bind the new function to whatever key the old function was bound to.

Here's a question with more information and here is Malabarba's article that is linked from one of the answers to that question. I found both of these very illuminating.

5
  • Thanks for explaining, that makes a lot of sense now. You're basically overriding the function bar from the local scope of my/foo which I will bind to my own key. So I will have both functions, the normal one and the override one from my key binding.
    – ritchie
    Commented Mar 11, 2023 at 3:37
  • You will only have one function bound to the key (either the old one or the new one). The point is that the new function uses the old function so they will both have to be around (i.e. in Emacs's memory). If you want to be able to call both quickly, then you will have to bind each to a separate key (but I doubt that is necessary).
    – NickD
    Commented Mar 11, 2023 at 3:41
  • I edited the answer a bit to clarify: it was indeed a bit confusing.
    – NickD
    Commented Mar 11, 2023 at 3:43
  • 1
    I understand. Thanks, I added (interactive "P") to make it work.
    – ritchie
    Commented Mar 11, 2023 at 4:25
  • Thanks! I added that to the answer..
    – NickD
    Commented Mar 11, 2023 at 10:36

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

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