How to pass arg in Emacs abstract function - emacs

I try to write a function to abstract which helm-imenu variant to use:
(defun my/helm-menu ()
"For Org mode buffers, show Org headlines.
For programming mode buffers, show functions, variables, etc."
(interactive)
(cond ((derived-mode-p 'org-mode)
(helm-org-in-buffer-headings))
(t
(helm-semantic-or-imenu))))
Though, when using it in a non-Org mode buffer, it fails, saying it needs one argument.
Indeed, helm-semantic-or-imenu requires arg.
How should I pass that?
Why is that working with a M-x helm-semantic-or-imenu: where is the argument?

Following Drew's piece of advice, this should do it:
(defun my/helm-menu (arg)
"For Org mode buffers, show Org headlines.
For programming mode buffers, show functions, variables, etc."
(interactive "P")
(cond ((derived-mode-p 'org-mode)
(helm-org-in-buffer-headings))
(t
(helm-semantic-or-imenu arg))))
At least, it works!

Related

Emacs lisp, How `interactive' reads buffer?

For example there is a function:
(defun testb (buf)
(interactive "bTest: ")
buf)
The question is how emacs internally reads a buffer for such interactive form?
Looks like it doesn't use read-buffer(or it calls the read-buffer as a C function(and doesn't look at symbol-function)?).
(flet ((read-buffer (&rest args) (current-buffer)))
(call-interactively #'testb))
It uses Lisp function read-buffer. The interactive spec you show is equivalent to this one:
(interactive (list (read-buffer "Test: " nil t)))
See the Elisp manual, node Using Interactive.
I guess you're referring to the fact that you got this error, or similar, which you get when you try to use flet on a built-in function. And yes, read-buffer is a subr, i.e., implemented in C. (symbol-function 'read-buffer) returns the built-in function #<subr read-buffer>.
Use ‘labels’, not ‘flet’, to rebind macro names

How can I jump to a definition without being queried in Emacs?

I have a problem when using Etags in Emacs. Everytime I tap \M+. to jump to a difinition point, a query is always popping up, like:
Find tag (default function_name):
And I have to tap 'Enter' to make sure of it.
But in most cases, I find I can choose the default one. So is there any method with which I can surpress this message?
I found the reason is because:
(defun find-tag (tagname &optional next-p regexp-p)
(interactive (find-tag-interactive "Find tag: "))
...
)
Why do I have to choose a tag? Why can not the default one just be the word under the point? Can I just remove this line? (interactive), or is there a good solution?
Going shortly through a couple of defuns in the etags sources via Emacs's awesome C-h f, one can find that the default tag to search is determined via a function named find-tag-default.
This means you could just define the following function:
(defun find-tag-under-point ()
(interactive)
(find-tag (find-tag-default)))
Then you can bind this to whatever key you want via define-key or global-set-key or local-set-key.
(The interactive form is always necessary if you want a function to be a "command" which can be called with M-x or bound to a key.)
You can write your own functionality over the find-tag (or any interactive function likewise)
(defun find-tag-under-point (&optional arg)
(interactive "P")
(cond ((eq arg 9)
(let ((current-prefix-arg nil))
(call-interactively 'find-tag)))
(arg
(call-interactively 'find-tag))
(t
(find-tag (find-tag-default)))))
(global-set-key (kbd "M-.") 'find-tag-under-point)
Then hotkey C-9M-. calls find-tag (old function) as usual, but the behaviour of find-tag-under-point (new-function) by default is what you want.

Emulate dabbrev-expand in hippie-expand, limiting to matching buffers

If I use dabbrev-expand for expansion, Emacs searches the current buffer, then other buffers with the same mode. This is handled by dabbrev-friend-buffer-function which by default is set to dabbrev--same-major-mode-p.
This works fine, but I'd like to use hippie-expand.
(setq hippie-expand-try-functions-list
'(try-expand-dabbrev
try-expand-dabbrev-all-buffers))
This pulls completions from all buffers, even the buffers that don't match my current major mode.
How can I use hippie-expand with dabbrev completions only coming from buffers using the same major-mode as the current buffer?
Quick and dirty solution: Copy the source code of the function try-expand-dabbrev-all-buffers to a new location, rename it (say) try-expand-dabbrev-all-buffers-same-mode, and replace the expression (buffer-list) with the expression:
(remove-if-not (lambda (x) (eq major-mode (with-current-buffer x major-mode)))
(buffer-list))
(You'll need to (require 'cl) to get remove-if-not, or else re-implement it in terms of mapcar and delq.)
Then, of course, replace try-expand-dabbrev-all-buffers with try-expand-dabbrev-all-buffers-same-mode in hippie-expand-try-functions-list.
You can get the source of try-expand-dabbrev-all-buffers using C-hf.
Based on Sean's excellent suggestion (and assuming you have the dash.el list utility library installed):
(autoload '--filter "dash" nil t)
;; only consider buffers in the same mode with try-expand-dabbrev-all-buffers
(defun try-expand-dabbrev-matching-buffers (old)
(let ((matching-buffers (--filter
(eq major-mode (with-current-buffer it major-mode))
(buffer-list))))
(flet ((buffer-list () matching-buffers))
(try-expand-dabbrev-all-buffers old))))

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))))

Define an emacs command that calls another emacs command (preserving interactive stuff)

How can I define an emacs command X that does something and then calls another emacs command Y and also copying the interactive interface of the command Y too?
I want to define an altenative version of query-replace with temporarilly toggled value of case-fold-search:
(defun alt-query-replace (a b c d e)
(interactive)
(let ((case-fold-search (not case-fold-search))
(query-replace a b c d e)))
This doesn't work. When I call alt-query-replace, it says "wrong number of arguments". I want the interactive interface of alt-query-replace to be the same as query-replace. Do I need to inspect the source code of query-replace or is there a general approach?
You may advise the original function, if you want to modify its behavior instead of calling a separate function.
From chapter 17.3 Around-Advice of the GNU Emacs Lisp Reference Manual:
Around-advice lets you “wrap” a Lisp
expression “around” the original
function definition.
(defadvice foo (around foo-around)
"Ignore case in `foo'."
(let ((case-fold-search t))
ad-do-it))
In your case, you can write:
(defadvice query-replace (around alt-query-replace (from-string to-string &optional delimited start end))
(let ((case-fold-search (not case-fold-search)))
ad-do-it))
(ad-activate 'query-replace)
Use call-interactively:
(defun alt-query-replace ()
(interactive)
(let ((case-fold-search (not case-fold-search)))
(call-interactively 'query-replace)))