I have written a macro that makes an alias and generates global keybinding based on the name.
I expect that if I add to my emacs config the following code for example:
(defkey-alias cool-function make-directory)
I will have command my-cool-function which creates a directory and a keybinding C-c c f to it. But after evaluation I have keybindings but have no command my-cool-function.
And if I do C-h k C-c c f I see:
C-c c f runs the command my-cool-function, which is an alias for `make-directory'.
I can not evaluate (my-cool-function) in scratch either.
But if I try to (macroexpand '(defkey-alias cool-function make-directory)) and then evaluate expanded s-expr it works.
What is the difference between calling macro and calling macroexpanding and then evaluation? And why alias is not callable?
Thank you.
Emacs version is GNU Emacs 24.2.1, Windows 7
The code:
;;; defining keys
(defun name-to-key(funname)
" works like:
'my-cool-function -> \"\C-cmcl\" "
(apply 'concat
"\C-c"
(mapcar (lambda(str)(substring str 0 1))
(split-string (symbol-name funname)
"-"))))
(defmacro defkey-alias(alias function)
"works like defalias but you should not quote symbols and sets global key mapping
Usage: (defkey-alias mkdir make-directory)"
(let ((myalias (make-symbol (concat "my-" (symbol-name alias)))))
`(progn
(defalias ',myalias ',function)
(global-set-key ,(name-to-key alias) ',myalias))))
UPDATED: Using (defun ...(interactive)(call-interactively 'function)) also does not work
The reason is simple: (make-symbol (concat "my-" (symbol-name alias))) returns a non-interned symbol. I.e. it returns a symbol whose name is my-cool-function and yet it is a different symbol from the one you get when you write (my-cool-function). So instead of make-symbol you want to use intern.
Related
So I'm trying to add something into some elisp mode hooks — specifically, I'd like to define a hook's prettify-symbols-alist and then specifically activate it by calling prettify-symbols-mode.
In any case, I'm getting org-babel to export the values into a pair of lists from a table, using pairlis to tie them together as an alist, and add-hook it into the desired mode using a anonymous function.
So, the thing is, right now if I use a global variable, like the following, it works:
(let ((token (quote ("not" "*" "/" "->" "map" "/=" "<=" ">=" "lambda")))
(code (quote (172 215 247 8594 8614 8800 8804 8805 955)))) ; Generated automatically using org-babel
(require 'cl)
(setq *globalvar (pairlis token code))
(add-hook 'emacs-lisp-mode-hook
(lambda ()
(setq prettify-symbols-alist *globalvar)
(prettify-symbols-mode 1))))
But if I try to not use a global variable, by doing it this way, it doesn't work:
(let ((token (quote ("not" "*" "/" "->" "map" "/=" "<=" ">=" "lambda")))
(code (quote (172 215 247 8594 8614 8800 8804 8805 955)))) ; Generated automatically using org-babel
(let (localv)
(require 'cl)
(setq localv (pairlis token code))
(add-hook 'emacs-lisp-mode-hook
(lambda ()
(setq prettify-symbols-alist localv)
(prettify-symbols-mode 1))))
I kind of know why: if I C-h v emacs-lisp-mode-hook, I'll see that it refers to whatever variable I used in the let form, which works when the variable exists, as in *globalvar, but not when I use localvar, which no longer exists outside of its let form. But I'm not sure how to force evaluation of the local variable itself, as I'm still struggling with a lot of concepts in elisp that aren't immediately clear to me.
What am I missing? Where am I going wrong here?
Okay, here's what I did in the end:
(let ((token (quote ("not" "*" "/" "->" "map" "/=" "<=" ">=" "lambda")))
(code (quote (172 215 247 8594 8614 8800 8804 8805 955))))
(require 'cl)
(lexical-let (localv)
(setq localv (pairlis token code))
(add-hook 'emacs-lisp-mode-hook
(lambda ()
(setq prettify-symbols-alist localv)
(prettify-symbols-mode 1)))))
I ended up using phils' suggestion to use lexical-let rather than Drew's suggestion mostly because I'm currently using org-babel to tangle code blocks into my source code (basically I'm using org-mode to organize my setting files), and there doesn't appear to be a way to set the lexical-binding file-local variable — according to this page, you need to set it as the first line (using ;; -*- lexical-binding: t -*-), and I can't find a way to do that yet.
In any case, thank you to everyone who helped me out on this question!
Start by setting lexical-binding to non-nil, or else localv will be a free variable in your hook function. Preferably, set lexical-binding as a file-local variable.
In addition, there is nothing in your code that makes localv buffer-local. Presumably you want to give it a value that is local to the buffer that is in that mode. The code that binds it should be evaluated in the mode (i.e., in the buffer) in question.
I like this solution. With lexical-let, the call to lambda (inside add-hook) generates a closure, as you can see if you type M-x ielm RET and emacs-lisp-mode-hook RET to examine its value.
You could also use old-school style backtick like this:
(add-hook 'emacs-lisp-mode-hook
`(lambda ()
(setq prettify-symbols-alist ',localv)
(prettify-symbols-mode 1)))
(EDIT)
Note the backtick before the lambda and (as tarikq mentioned) the quote-comma before localv.
I think you got the meaning!
Actually, instead of "expand this then quote it", I would say "insert its value (from the lexical environment), then quote it".
If you try something like:
(macroexpand '`(lambda ()
(setq prettify-symbols-alist ',localv)
(prettify-symbols-mode 1)))
then you will get what lisp will actually do at run time:
(cons 'lambda
(cons nil
(cons
(list 'setq 'prettify-symbols-alist (list 'quote localv))
'((prettify-symbols-mode 1)))))
and you will see how the whole list is constructed, and how localv is normally evaluated, i.e. not quoted (compare with the symbols 'setq and 'prettify-symbols-alist and the list '((prettify-symbols-mode 1)))
I'd like to implement a command that types the first few characters into an existing command and lets me type the rest.
For example, a variant of icicle-execute-extended-command that starts with "icicle-" already entered.
I have tried:
keyboard macros
fail (even on simple things like M-x i c i c l e s) for no apparent reason.
functions
calling icicle-execute-extended-command block the command sequence
How would I go about doing this in a generalized manner?
Nice question.
Here's something generic you can try:
(defun no-mondays ()
(interactive)
(minibuffer-with-setup-hook
(lambda()
(insert "monday"))
(call-interactively 'query-replace)))
And here's a refactoring:
(defun with-initial-minibuffer (str fun)
`(lambda ()
(interactive)
(minibuffer-with-setup-hook
(lambda ()
(insert ,str))
(call-interactively ',fun))))
(defalias 'no-weekends
(with-initial-minibuffer
"\\(?:satur\\|sun\\)day"
'query-replace-regexp))
If you are calling completing-read yourself in your command definition, then just pass the text to insert as the INITIAL-INPUT argument. That's what it's for.
If you use icicle-define-command or icicle-define-file-command (so that your command will be a multi-command), then same thing: pass the INITIAL-INPUT arg.
If you use such a macro, be sure to put something like this in the file that defines the command, so the macro definition is available a byte-compilation time:
(eval-when-compile
(or (condition-case nil
(load-library "icicles-mac") ; Use load-library to ensure latest .elc.
(error nil))
(require 'icicles-mac)))
When I do C-h f or C-h v, Help tells me in which file the symbol is defined or where it will be autoloaded from. How can I find the same information programmatically?
Some digging reveals that
(find-lisp-object-file-name object type)
Should do the trick. As an example:
(find-lisp-object-file-name 'goto-line 'function)
;; => "/usr/local/Cellar/emacs/24.3/share/emacs/24.3/lisp/simple.el"
EDIT: How I discovered this information:
First I did C-h k C-h f to figure out what C-h f is bound to. The result is describe-function, so let's do C-h f describe-function to see the source for that. I noticed that it was essentially an interactive wrapper around describe-function-1, so I jumped to the source for that. There's a lot of stuff in there, but the pertinent line is:
(file-name (find-lisp-object-file-name function def))
Revealing that find-lisp-object-file-name is the function used to do this work internally.
To add to James Porter's answer
;;; run from: emacs -q
(require 'cl) ; for incf
(print (list
;; goto-line is a function defined in simple.el
(find-lisp-object-file-name 'goto-line (symbol-function 'goto-line))
;; print is a function defined in C
(find-lisp-object-file-name 'print (symbol-function 'print))
;; rx is an autoload from rx.el
(find-lisp-object-file-name 'rx (symbol-function 'rx))
;; incf is an alias for cl-incf defined in cl.el
(find-lisp-object-file-name 'incf (symbol-function 'incf))
;; cl-incf is defined in cl-lib.el
(find-lisp-object-file-name 'cl-incf (symbol-function 'cl-incf))))
;; => ("c:/run/Emacs/lisp/simple.el" C-source
;; "c:/run/Emacs/lisp/emacs-lisp/rx.el" "c:/run/Emacs/lisp/emacs-lisp/cl.el"
;; "c:/run/Emacs/lisp/emacs-lisp/cl-lib.el")
(print (list
;; print-circle is a variable defined in C
(find-lisp-object-file-name 'print-circle 'defvar)
;; indent-line-function is a variable defined in indent.el
(find-lisp-object-file-name 'indent-line-function 'defvar)))
;; => (C-source "c:/run/Emacs/lisp/indent.el")
I can run a shell command quickly by hitting M-!. One thing I'd like to do is perform shell quick operations on the current file. An example would be checking the file out through perforce:
M-! p4 edit buffer-file-name RET
(Yes there are perforce integrations, but I'm more interested in the minishell/variable problem rather than a specific workflow)
Of course, the buffer-file-name variable is not evaluated before the command is sent to the shell.
Is there an easy on-the-fly way to do this? Or will I have to roll a custom elisp function?
It seems current Emacs has something built-in to achieve the desired result, after M-! (shell-command) press <down>, you will get the file name you are currently visiting on the prompt. Now you can edit it to add the command you want to run on it.
In dired-mode it will give you the file your cursor is currently on.
Indeed using C-u M-: is almost right. I'm not so sure about using shell-quote-argument in eval-to-shell-argument since it only works on strings making it impossible to use eval-to-shell-argument to insert a number or a symbol. You could try something like:
(defun sm-minibuffer-insert-val (exp)
(interactive
(list (let ((enable-recursive-minibuffers t))
(read-from-minibuffer "Insert: "
nil read-expression-map t
'read-expression-history))))
(let ((val (with-selected-window (minibuffer-selected-window)
(eval exp)))
(standard-output (current-buffer)))
(prin1 val)))
and then bind this function in your minibuffer with (define-key minibuffer-local-map [?\M-:] 'sm-minibuffer-insert-val).
Of course, if the only thing you ever want to insert is the buffer-file-name, then your execute-shell-command-on-buffer is simpler.
I did roll my own elisp function, and it looks like this:
(defun execute-shell-command-on-buffer (shell-command-text)
(interactive "MShell command:")
(shell-command (format shell-command-text (shell-quote-argument buffer-file-name)))
)
https://gist.github.com/2367513
I bound it to M-", so now my example can be completed with:
M-"p4 edit %sRET
I won't accept this as the answer, because I did ask for solutions that don't require a function.
You can use C-u M-: (eval-expression with a universal prefix argument) to evaluate any Lisp expression and insert its value at point in the current buffer (including minibuffers, as long as you have enable-recursive-minibuffers set to a non-nil value).
In your example: C-u M-: buffer-file-name RET.
Note that the result of the expression is printed in Lisp form: that is, quoted in such a way that a subsequent call to read would construct an equal Lisp value. For strings, this means enclosing in double quotes, which will probably be interpreted as you expect by the inferior shell. However, you may run into problems with strings that contain special characters, which need different escaping by Elisp and the shell.
The more correct way uses shell-quote-argument, as in phils' solution. Here's a quick defun that reads a Lisp expression and inserts its value at point as a properly quoted shell word:
(defun eval-to-shell-argument (form)
(interactive "XEval: ")
(insert (shell-quote-argument form)))
The read-and-evaluate step happens automatically by using an "X" as the argument to interactive.
Edited to add: As #tenpn notes, the above solution doesn't work for inserting buffer-local variables like buffer-file-name in a minibuffer like the one M-! pops up (more precisely, it inserts the buffer-local value of the minibuffer, which is unlikely to be useful). Here is a revised version which seems to work. If the minibuffer is active, it makes the buffer of the previously-selected window temporarily active while reading and evaluating an expression.
Final edit: From #Stefan's answer I see that I should have used (minibuffer-selected-window) to find the previously-selected window. I've also added a (format "%s" ..) to allow inserting non-string values, while still quoting special characters in strings. Here's the final version:
(defun eval-to-shell-argument ()
(interactive)
(let* ((buffer
(if (minibufferp)
(window-buffer (minibuffer-selected-window))
(current-buffer)))
(result
(with-current-buffer buffer
(eval-minibuffer "Eval: "))))
(insert (shell-quote-argument (format "%s" result)))))
You can't do that with M-!, but you can evaluate arbitrary elisp from the minibuffer, so writing a function isn't strictly necessary:
M-: (shell-command (format "p4 edit %s" (shell-quote-argument buffer-file-name))) RET
In this case however, I think eshell is what you want to use:
M-x eshell-command RET p4 edit (eval buffer-file-name) RET
Edit: Except unfortunately that doesn't work, as the *eshell cmd* buffer is selected when that is evaluated. One solution would be:
M-x eshell-command RET p4 edit (eval buffer-file-name (other-buffer nil t)) RET
(Not quite as elegant, sorry.)
Everyone seems to be rolling their own version, so here's mine -- it will substitue the current filename or marked dired-files or current dired file wherever a % is in the shell command. It follows the same conventions as M-! so I bind it to that.
(defun my-shell-command (command &optional output-buffer error-buffer)
"Run a shell command with the current file (or marked dired files).
In the shell command, the file(s) will be substituted wherever a '%' is."
(interactive (list (read-from-minibuffer "Shell command: "
nil nil nil 'shell-command-history)
current-prefix-arg
shell-command-default-error-buffer))
(cond ((buffer-file-name)
(setq command (replace-regexp-in-string "%" (buffer-file-name) command nil t)))
((and (equal major-mode 'dired-mode) (save-excursion (dired-move-to-filename)))
(setq command (replace-regexp-in-string "%" (mapconcat 'identity (dired-get-marked-files) " ") command nil t))))
(shell-command command output-buffer error-buffer))
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)))