Emacs lisp - can a defun reference itself somehow? [duplicate] - emacs

(defun foo ()
(send-to-debug-log "Error. Function terminated." (get-current-function-name)))
I currently do this:
(defun foo ()
(send-to-debug-log "Error. Function terminated." 'foo)))
Hard coding the function name seems not to be a good practice.
Any suggestion to implement the get-current-function-name or get-function-name-that-call-me.

(defun foo ()
(send-to-debug-log "Error. Function terminated." (nth 1 (backtrace-frame 2))))

You could try something like:
(defconst my-defun-macros '(defun cl-defun defmacro cl-defmacro))
(defun my-add-defun-name (orig-fun name &rest args)
(let* ((my-current-defun-name (if (consp name) (cadr name) name))
(macroexpand-all-environment
(cons (cons 'my-get-current-function-name
(lambda () my-current-defun-name))
macroexpand-all-environment))
(code (apply orig-fun name args)))
(macroexpand-all code macroexpand-all-environment)))
(defmacro my-get-current-function-name ()
"Return current defun's name.
Only works within the advised macros in `my-defun-macros'."
(error "my-get-current-function-name used outside of a defun"))
(dolist (m my-defun-macros)
(advice-add m :around #'my-add-defun-name))

Here's something that seems to work pretty well:
(defun calling-function ()
(let ((n 6) ;; nestings in this function + 1 to get out of it
func
bt)
(while (and (setq bt (backtrace-frame n))
(not func))
(setq n (1+ n)
func (and bt
(nth 0 bt)
(nth 1 bt))))
func))
If you call it in a lambda, you get the whole lambda. It works with apply, funcall, and eval.
I think the most simple, robust way is to just explicitly write the function name, as you're doing now.

With the package which-function-mode (in melpa), you can programmatically call the function
(Which-function)
more info:
https://www.masteringemacs.org/article/which-function-mode
http://emacsredux.com/blog/2014/04/05/which-function-mode/

Related

Emacs metaprogramming, dynamically define methods

I am trying to define some helper functions to quickly jump to different projects from within emacs. I started by defining a macro as follows
(defmacro project-alias (name path)
`(defun ,name ()
(interactive)
(cd ,path)))
And this works great I can (project-alias foo "~/bar") no problem. The problem comes when I try and apply this macro over a list of tuples.
(setq projects '((foo . "~/foo")
(bar . "~/bar")))
(dolist (p projects)
(project-alias (car p) (cdr p)))
The above code errors with
Debugger entered--Lisp error: (wrong-type-argument symbolp (car p))
defalias((car p) (lambda nil (interactive) (cd (cdr p))))
I have tried passing the first argument in as a string and calling intern to get the symbol representation out with no joy, and I've also tried defining my macro to accept the string form and that doesn't work either
What am I doing wrong?
If your use of the macro involves evaluating sexps to produce the name and path, then it needs to evaluate the sexps:
(defmacro project-alias (name path)
`(defun ,(eval name) () (interactive) (cd ,(eval path))))
Alternatively, use a function:
(defun project-alias (name path)
(eval `(defun ,name () (interactive) (cd ,path))))
You could do either
(defun project-alias-f (name path)
(eval `(defun ,name ()
(interactive)
(cd ,path))))
(dolist (p projects)
(project-alias-f (car p) (cdr p)))
or
(dolist (p projects)
(eval `(project-alias ,(car p) ,(cdr p))))
Macro arguments are passed un-evaluated. (Macros could not otherwise do what they can do.)
So your arguments are literally the forms (car p) and (cdr p) (as opposed to, for instance, foo and "~/foo").
Here's another take on it, with no macros nor eval:
;; -*- lexical-binding:t -*-
(defun project-alias-f (name filename)
(defalias name (lambda () (interactive) (cd filename)))
(dolist (p projects)
(project-alias-f (car p) (cdr p)))
This is not an answer to your macro problem, but an alternative solution to your desire to jump between projects.
In my init.el file, I have (amongst other things)
(set-register ?A '(file . "~/.aliases"))
(set-register ?E '(file . "~/.emacs.d/init.el"))
(set-register ?H '(file . "~/.hgrc"))
(set-register ?T '(file . "~/.TODO.org"))
Then I can use jump-to-register (C-x r j) to jump to one of these files when I wish to edit the file (or do something with one of the unlisted projects). Because a file/folder is stored in the register (rather than a window config say), emacs will open the file or folder it finds in the register.

How to call describe-function for current-word in Emacs?

I want to write an Emacs function that calls describe-function for current-word. And if there is no function named current-word then it calls describe-variable.
I tried to write it, but I couldn't even call describe-function for current-word...
(defun describe-function-or-variable ()
(interactive)
(describe-function `(current-word)))
How can I write it?
Something like this should work:
(defun describe-function-or-variable ()
(interactive)
(let ((sym (intern-soft (current-word))))
(cond ((null sym)
"nothing")
((functionp sym)
(describe-function sym))
(t
(describe-variable sym)))))
Here's a more general function:
(defun describe-function-or-variable ()
(interactive)
(let ((sym (intern-soft (current-word))))
(unless
(cond ((null sym))
((not (eq t (help-function-arglist sym)))
(describe-function sym))
((boundp sym)
(describe-variable sym)))
(message "nothing"))))
It works for special forms, e.g. and, as well as for macros, e.g. case.
It also makes sure that the varible is bound, before trying to describe it.

Emacs proper cl-flet indentation?

The current indentation for cl-flet seems really ugly to me.
See for instance:
(defun foo (lst)
(cl-flet ((unusually-long-bar (x)
(1+ x)
(1+ x)
(1+ x)))
(mapcar #'unusually-long-bar lst)))
I'd like to set it to something more sensible, like:
(defun foo (lst)
(cl-flet ((unusually-long-bar (x)
(1+ x)
(1+ x)
(1+ x)))
(mapcar #'unusually-long-bar lst)))
How can I do this?
The following should work:
(setq lisp-indent-function 'common-lisp-indent-function)
(eval-after-load "cl-indent"
'(progn
(put 'cl-flet 'common-lisp-indent-function
(get 'flet 'common-lisp-indent-function))
))
By way of addition to Sabof's answer, here is a snippet which copies indentation rules from all Common Lisp symbols to their cl- prefixed Emacs equivalents, when the latter exist:
(load-library "cl-indent") ; defines the common-lisp-indent-function properties
(cl-loop for symbol being the symbols
for cl-indent-rule = (get symbol 'common-lisp-indent-function)
for elisp-equivalent = (intern-soft (concat "cl-" (symbol-name symbol)))
when (and cl-indent-rule elisp-equivalent (fboundp elisp-equivalent))
do (put elisp-equivalent 'common-lisp-indent-function cl-indent-rule))

flet works, but with obsolete message; cl-flet does not work

I'm trying to temporarily turn off the yes-or-no-p within a function that is defined elsewhere and then restore things to the way they were. Using flet works, but creates a *compile-log* buffer telling me that it is obsolete and to use cl-flet instead. However, cl-flet doesn't seem to work with this attempt at defadvice -- i.e., nothing happens and the yes-or-no-p remains active. Any ideas on how to avoid the error message and make this work also?
(defun function-without-confirmation ()
(defadvice elmo-dop-queue-flush (around stfu activate)
(flet ((yes-or-no-p (&rest args) t)
(y-or-n-p (&rest args) t))
ad-do-it))
. . . .
(ad-unadvise 'elmo-dop-queue-flush)
)
I cannot take credit for the answer, because that was solved by wvxvw, so I'll put the relevant fix underneath the original question. The new macro is called lawlist-flet instad of flet, and the obsolete line has been commented out:
(defmacro lawlist-flet (bindings &rest body)
"Make temporary overriding function definitions.
This is an analogue of a dynamically scoped `let' that operates on the function
cell of FUNCs rather than their value cell.
If you want the Common-Lisp style of `flet', you should use `cl-flet'.
The FORMs are evaluated with the specified function definitions in place,
then the definitions are undone (the FUNCs go back to their previous
definitions, or lack thereof).
\(fn ((FUNC ARGLIST BODY...) ...) FORM...)"
(declare (indent 1) (debug cl-flet)
;; (obsolete "use either `cl-flet' or `cl-letf'." "24.3")
)
`(letf ,(mapcar
(lambda (x)
(if (or (and (fboundp (car x))
(eq (car-safe (symbol-function (car x))) 'macro))
(cdr (assq (car x) macroexpand-all-environment)))
(error "Use `labels', not `flet', to rebind macro names"))
(let ((func `(cl-function
(lambda ,(cadr x)
(cl-block ,(car x) ,#(cddr x))))))
(when (cl--compiling-file)
;; Bug#411. It would be nice to fix this.
(and (get (car x) 'byte-compile)
(error "Byte-compiling a redefinition of `%s' \
will not work - use `labels' instead" (symbol-name (car x))))
;; FIXME This affects the rest of the file, when it
;; should be restricted to the flet body.
(and (boundp 'byte-compile-function-environment)
(push (cons (car x) (eval func))
byte-compile-function-environment)))
(list `(symbol-function ',(car x)) func)))
bindings)
,#body))
And, here is the modified function that eliminates the error message relating to flet being obsolete.
(defun function-without-confirmation ()
(defadvice elmo-dop-queue-flush (around stfu activate)
(lawlist-flet ((yes-or-no-p (&rest args) t)
(y-or-n-p (&rest args) t))
ad-do-it))
. . . .
(ad-unadvise 'elmo-dop-queue-flush)
Here's how I'd recommend you do it:
(defvar stfu-inhibit-yonp nil)
(defadvice yes-or-no-p (around stfu activate)
(if stfu-inhibit-yonp (setq ad-return t) ad-do-it))
(defadvice y-or-n-p (around stfu activate)
(if stfu-inhibit-yonp (setq ad-return t) ad-do-it))
(defadvice elmo-dop-queue-flush (around stfu activate)
(let ((stfu-inhibit-yonp t))
ad-do-it))
Contrary to CL's flet this will make it clear (e.g. in C-h f yes-or-no-p) that something's going on with yes-or-no-p.

How to manage underlying interface changes in elisp

Somewhere between emacs 23.1 and 24.1, the interface of url-retrieve changed. In emacs 23.1, it looks like this:
(url-retrieve URL CALLBACK &optional CBARGS)
In version 24.1, it looks like this:
(url-retrieve URL CALLBACK &optional CBARGS SILENT INHIBIT-COOKIES)
I have an emacs package that uses this function. I'd like to take advantage of the new SILENT argument on emacs 24.1, while maintaining backwards compatibility with older versions of emacs that don't support it.
What's the best way to manage this? Can I grab the maximum number of arguments at runtime?
You can use this function to get the argument list:
(defun my-get-arglist (obj)
;; code taken from disassemble-internal
(let ((macro 'nil)
(name 'nil)
(doc 'nil)
args)
(while (symbolp obj)
(setq name obj
obj (symbol-function obj)))
(if (subrp obj)
(error "Can't disassemble #<subr %s>" name))
(if (and (listp obj) (eq (car obj) 'autoload))
(progn
(load (nth 1 obj))
(setq obj (symbol-function name))))
(if (eq (car-safe obj) 'macro) ;handle macros
(setq macro t
obj (cdr obj)))
(if (and (listp obj) (eq (car obj) 'byte-code))
(setq obj (list 'lambda nil obj)))
(if (and (listp obj) (not (eq (car obj) 'lambda)))
(error "not a function"))
(if (consp obj)
(if (assq 'byte-code obj)
nil
(setq obj (byte-compile obj))))
(cond ((consp obj)
(setq obj (cdr obj)) ;throw lambda away
(setq args (car obj)) ;save arg list
(setq obj (cdr obj)))
((byte-code-function-p obj)
(setq args (aref obj 0)))
(t (error "Compilation failed")))
args))
You could check emacs-major-version and make sure it's >= 24.
(defun try-call-with-more-args (function a b c d)
(condition-case var
(progn
(funcall function a b c d)
(message "there was no error"))
(wrong-number-of-arguments (funcall function a b c))))
(try-call-with-more-args #'message "b = %d, c = %d" 1 2 3)
Even though the one posted by Trey Jackson is much smarter, this is simpler and, actually has a good chance of working, even if you are targeting a native C function :)
If it's the older emacs version, write a wrapper around the older function:
(let ((orig-fun #'symbol-name-of-orig-fun))
(defun symbol-name-of-orig-fun (arglist of new function interface)
(declare (ignore new function interface))
(funcall orig-fun arglist of)))
This generates a lexical closure that stores the reference of the old function within the new function. I don't know the emacs dialect, but I would bet this pattern could be used in emacs. I use it every once in a while in common lisp.