How do I make an Emacs keybinding to set a variable? - emacs

I've got a variable in Emacs called my-var that I'd like to set whenever I press C-v. How do I do that? I tried this:
(defun set-my-var (value)
"set my var"
(interactive)
(defvar my-var value
"a variable of mine")
)
(global-set-key "\C-v" 'set-my-var)
But that fails:
call-interactively: Wrong number of arguments: (lambda (value) "set my var"
(interactive) (defvar my-var value "a variable of mine")), 0

Actually, defvar doesn't do what you think it does either: it only changes the value IF there was no value before. Here's a chunk that does what you're looking for, using the CTRL-u argument:
(defun set-my-var (value)
"Revised version by Charlie Martin"
(interactive "p")
(setq my-var value))
and here's an example, code from the *scratch* buffer
(defun set-my-var (value)
"Revised version by Charlie Martin"
(interactive "p")
(setq my-var value)) ; do ^J to evaluate the defun form, showing return in the buffer.
set-my-var
(global-set-key "\C-v" 'set-my-var)
set-my-var
;Another ^J
;;; Now, what's the value after ^U^V?
my-var
4
;;; Now ^U 8 ^V
my-var
8
;;; now ^U^U^V
my-var
16

It's in the argument. Look over at the text I just posted about (interactive). When you bind set-my-var to a key, it's looking for an argument, but since you used (interactive) there's no argument to be had. What you wanted is something like (interactive "p") to get the CTRL-u argument, or (interactive "M") to get a string.
Read the EMACS Lisp manual on "Using Interactive."

A couple of other hints:
CTRL-v is a standard binding and
pretty heavily used (scroll-up).
You'd be better off finding
something that's not otherwise used.
Canonically, those would be added to
the CTRL-c keymap.
Don't get in the habit of treating
parens as if they were C braces.
It's better (more customary) LISP
style for the rest of us who might
read your code to just close all the
parens at the end.

Related

Emacs Copy Current Line

For some reason, copying lines in Emacs is very unintuitive and difficult. If I'm doing something wrong here, please let me know. It is surprising that Emacs does not have this by default somewhere.
I am trying to write a function that copies a line. I always used to have:
(global-set-key (kbd "C-x c") "\C-a\C- \C-n\M-w")
This is a little annoying since it copies any new line after the line. I decided to change it to:
(global-set-key (kbd "C-x c") "\M-m\C- \C-e\M-w")
Now, I saw: http://www.emacswiki.org/emacs/CopyingWholeLines and it appears that their copy-line function prints a message with the number of lines it copied. I am trying to insert that message into my global-set-key above but it is not working. Basically, I am unable to run a raw sequence as above in a function. So I conveyed each keystroke into a function and did this:
(defun copy-line ()
(interactive)
(kill-ring-save
(back-to-indentation)
(move-end-of-line 1))
(message "1 line copied"))
;; optional key binding
(global-set-key "\C-c\C-k" 'copy-line)
This however, throws a wrong number of arguments error.
My first question: How can I put (message "1 line copied") into my global-set-key above?
My second question: using the standard copy-line found in the link above:
(defun copy-line (arg)
"Copy lines (as many as prefix argument) in the kill ring"
(interactive "p")
(kill-ring-save (line-beginning-position)
(line-beginning-position (+ 1 arg)))
(message "%d line%s copied" arg (if (= 1 arg) "" "s")))
From the message, it appears that you can have multiple lines copied. However, when selecting multiple lines and copying, only one is copied. Why is the message structured in this way? How can it select multiple lines?
Here's your function, fixed:
(defun copy-line ()
(interactive)
(save-excursion
(back-to-indentation)
(kill-ring-save
(point)
(line-end-position)))
(message "1 line copied"))
The problem was that back-to-indentation doesn't return point.
As for the other function, it will copy multiple lines when called with
a prefix argument, e.g. C-u or M-5.
Here's a shorter version of the simple function:
(defun copy-line ()
(interactive)
(kill-ring-save (point-at-bol) (point-at-eol))
(message "1 line copied"))
For the multiline copy version that you cited, use the prefix to indicate how many lines you want copied (as the other answer suggests). So, with your keybinding of C-c C-k, do the following to copy, say, 3 lines: C-u 3 C-c C-k.

How to know when to use parenthesis

I am reading through An Introduction to Programming in Emacs Lisp, and I see the following:
This code works:
(message "The name of this buffer is: %s." (buffer-name))
while this one fails:
(message "The name of this buffer is: %s." buffer-name)
However, this code works:
(message "The value of fill-column is %d." fill-column)
while this one fails:
(message "The value of fill-column is %d." (fill-column))
My question is why? What is the difference between buffer-name and fill-column? How do I know when to use parentheses?
Simply put - buffer-name is a function (that returns a string) and fill-column is a variable (that evaluates to an integer).
Functions calls in all Lisp dialects should be surrounded by parentheses.
To see details about a function in Emacs press C-h f function-name RET.
To see details about a variable in Emacs press C-h v variable-name RET.

Emacs / Elisp / Closure / Toggle

I currently have the following:
(defun my-hide-code ()
(interactive)
(set-selective-display 2))
(defvar my-keys-minor-mode-map (make-keymap) "my-keys-minor-mode keymap.")
(define-key my-keys-minor-mode-map (kbd "<f2>") 'my-hide-code)
(define-minor-mode my-keys-minor-mode
"use"
t " my-keys" 'my-keys-minor-mode-map)
(my-keys-minor-mode 1)
Now, I want to toggle between
(set-selective-display 2)
and
(set-selective-display 'nil)
Now, if I was in scheme, I would just create a s closure, and the closure would store a state variable, which would allow me to know which state I was in, and how to toggle.
However, Elisp is apparently a lisp-2 where variables and functions have different scope. Given this, how do I create closures / have be a toggle key?
Emacs 24 supports lexical binding, so real closures can be written.
See:
What are the new rules for variable scoping in Emacs 24?
Just be aware that dynamic binding is a very large part of what makes Emacs so customizable, so use lexical binding with due care if you are writing something which may be of use to others -- ensure that you defvar any variable which might potentially be useful for someone to override (ensuring, of course, that you think up all those potential use-cases that don't apply to you! :)
For Emacs < 24, see:
http://www.emacswiki.org/emacs/FakeClosures
Other Stack Overflow Q&As
selective-display itself is the variable that contains the state you need to check, so you can write
(defun my-toggle-selective-display ()
(interactive)
(if selective-display
(set-selective-display nil)
(set-selective-display 2)))
As for closures in general, I had need of them for a project long ago and resorted to inserting uninterned symbols into a function definition using backquote substitution to avoid global variables. E.g.
(fset 'toggler
(let ((toggle-var (make-symbol "toggle")))
(set toggle-var nil)
`(lambda () (interactive)
(cond (,toggle-var
(setq ,toggle-var nil))
(t
(setq ,toggle-var t)))
(message "toggle is %s" ,toggle-var))))
which when run produces
M-x toggler
toggle is t
M-x toggler
toggle is nil
M-x toggler
toggle is t
M-: (boundp 'toggle)
nil
I guess this qualifies as a kludge, but I know of no other way to get anything like a closure in Emacs-Lisp.
Here is a link to an answer I gave elsewhere about how to create real (not fake)
closures in Elisp:
https://stackoverflow.com/a/14088107/1857114
I use a macro I call defun****. It works in Emacs 24.2.1 but I'm not sure what
earlier versions it works for. Presumably the **defun special form will be
upgraded some time in the future and the macro will be unnecessary.

Passing Emacs variables to minibuffer shell commands

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 to mark the text between the parentheses in Emacs?

Any such function or elisp script? I want the equivalent function as vi" sequence in vim.
Try the key sequence C-M-u C-M-SPC (i.e., while holding the Control and Meta keys, press u and Space in sequence), which executes the commands backward-up-sexp and mark-sexp.
Edit:
I made a mistake: backward-up-sexp doesn't exist in standard Emacs. I wrote it exactly because of the problem mentioned in lkahtz's comment, that the existing function backward-up-list won't work when point is between double quotes.
(defun backward-up-sexp (arg)
(interactive "p")
(let ((ppss (syntax-ppss)))
(cond ((elt ppss 3)
(goto-char (elt ppss 8))
(backward-up-sexp (1- arg)))
((backward-up-list arg)))))
(global-set-key [remap backward-up-list] 'backward-up-sexp)
expand-region (which is bound to C-=) works great.
Xah Lee has an emacs-lisp function which achieves this called xah-select-text-in-quote. It is available from his website:
Select text between the nearest left and right delimiters.
Delimiters here includes the following chars: \"<>(){}[]“”‘’‹›«»「」『』【】〖〗《》〈〉〔〕().
This command does not properly deal with nested brackets.