Let's say I bind the key to a certain function as follows:
(global-set-key (kbd "C-c =") 'function-foo)
Now, I want the key binding to work as:
After I press C-c = for the first time, if I want to repeat the function-foo, I don't need to press C-c again, but simply repeat pressing =. Then, after I call the function-foo for enough times, I can just press keys other than = (or explicitly press C-g) to quit.
How to do this?
This may be the thing you are looking for:
(defun function-foo ()
(interactive)
(do-your-thing)
(set-temporary-overlay-map
(let ((map (make-sparse-keymap)))
(define-key map (kbd "=") 'function-foo)
map)))
There's a smartrep.el package that does exactly what you need. The documentation is a bit scarce but you can get a grip of how it's supposed to be used by looking into numerous emacs configs found on github. For example (taken from here):
(require 'smartrep)
(smartrep-define-key
global-map "C-q" '(("n" . (scroll-other-window 1))
("p" . (scroll-other-window -1))
("N" . 'scroll-other-window)
("P" . (scroll-other-window '-))
("a" . (beginning-of-buffer-other-window 0))
("e" . (end-of-buffer-other-window 0))))
This is what I use. I like it because you don't have to specify the repeating key.
(require 'repeat)
(defun make-repeatable-command (cmd)
"Returns a new command that is a repeatable version of CMD.
The new command is named CMD-repeat. CMD should be a quoted
command.
This allows you to bind the command to a compound keystroke and
repeat it with just the final key. For example:
(global-set-key (kbd \"C-c a\") (make-repeatable-command 'foo))
will create a new command called foo-repeat. Typing C-c a will
just invoke foo. Typing C-c a a a will invoke foo three times,
and so on."
(fset (intern (concat (symbol-name cmd) "-repeat"))
`(lambda ,(help-function-arglist cmd) ;; arg list
,(format "A repeatable version of `%s'." (symbol-name cmd)) ;; doc string
,(interactive-form cmd) ;; interactive form
;; see also repeat-message-function
(setq last-repeatable-command ',cmd)
(repeat nil)))
(intern (concat (symbol-name cmd) "-repeat")))
You want your function-foo to use set-temporary-overlay-map.
In addition to what #juanleon suggested, which uses set-temporary-overlay-map, here is an alternative that I use quite a bit. It uses standard library repeat.el.
;; This function builds a repeatable version of its argument COMMAND.
(defun repeat-command (command)
"Repeat COMMAND."
(interactive)
(let ((repeat-previous-repeated-command command)
(last-repeatable-command 'repeat))
(repeat nil)))
Use that to define different repeatable commands. E.g.,
(defun backward-char-repeat ()
"Like `backward-char', but repeatable even on a prefix key."
(interactive)
(repeat-command 'backward-char))
Then bind such a command to a key with a repeatable suffix, e.g., C-c = (for C-c = = = =...)
See this SO post for more information.
Related
I have been trying to make a few changes to ido-mode to make it more useful. One of the things which I have been trying to do is to remap some of the keys which I use in ido-find-file. The main one is that I want to use C-d to call the ido-enter-dired function instead of having to press C-f+C-d which does the same thing.
This is my ido setup so far:
(defun ali/ido ()
"My configuration for ido-mode"
(require 'ido)
(setq ido-create-new-buffer 'always)
;; Making sure that ido works in M-x
(global-set-key
"\M-x"
(lambda ()
(interactive)
(call-interactively
(intern
(ido-completing-read
"M-x "
(all-completions "" obarray 'commandp))))))
;; Ido keybindings
(defun ido-keybindings ()
(define-key ido-completion-map (kbd "C-d") 'ido-enter-dired))
(add-hook 'ido-setup-hook 'ido-keybindings)
(ido-everywhere t)
(ido-mode 1))
However whenever, I try to use C-d in ido-find-file I always get this error:
Debugger entered--Lisp error: (error "Command attempted to use minibuffer while in minibuffer")
When called with the minibuffer active, your command uses a recursive minibuffer to read input using ido-completing-read.
Use this as your command instead:
(lambda ()
(interactive)
(let ((enable-recursive-minibuffers t)) ; <=====================
(call-interactively
(intern
(ido-completing-read
"M-x "
(all-completions "" obarray 'commandp))))))
C-h v enable-recursive-minibuffers tells us:
enable-recursive-minibuffers is a variable defined in C source code.
Its value is nil
Documentation:
Non-nil means to allow minibuffer commands while in the minibuffer.
This variable makes a difference whenever the minibuffer window is active.
Also see minibuffer-depth-indicate-mode, which may be handy if this
variable is non-nil.
You can customize this variable.
Need to insert some text in the ido mini-buffer during ido-find-file using a defun bound to a key chord. This behavior is the same as if the following sequence was typed interactively, C-x C-f, followed by C-e to edit the mini-buffer, followed by deleting the current text then typing or yanking the text that I want to insert. To that end I have run this defun and just after ido-find-file:
(global-set-key [f10] 'mytest)
(defun mytest ()
(interactive)
(ido-edit-input) ;#1
(delete-minibuffer-contents) ;#2
(insert "C:/users/family/" )) ;#3
My expectation is that the mini-buffer would contain the given string (#3) after one invocation of mytest. However it seems as if I need to run mytest 2x to get the inserted string into the mini-buffer. After the first invocation the ido mini-buffer is in edit mode, however is as if only the #1 was run (the original text is still present). After the 2nd invocation of mytest the ido mini-buffer seems as if #1, #2 and #3 have been executed.
I am using a stand-alone mini-buffer which is part of the one-on-one library.
Running GNU emacs 24.4 on Windows.
Here is my custom configuration of ido, which I run at emacs startup.
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;IDO Config
(setq ido-enable-flex-matching t)
;
(setq ido-everywhere t)
(ido-everywhere 1)
;
(require 'ido)
(ido-mode 1)
;
(setq ido-auto-merge-work-directories-length -1)
;
;https://github.com/gempesaw/ido-vertical-mode.el/blob/master/ido-vertical-mode.el
;; Display ido results vertically, rather than horizontally
(setq ido-decorations (quote ("\n-> " "" "\n " "\n ..." "[" "]" " [No match]" " [Matched]" " [Not readable]" " [Too big]" " [Confirm]")))
(defun ido-disable-line-trucation ()
(set (make-local-variable 'truncate-lines) nil))
(add-hook 'ido-minibuffer-setup-hook 'ido-disable-line-trucation)
;
(setq ido-ignore-buffers '("\\` " "^\*Mess" "^\*Back" ".*Completion"
"^\*Ido" "^\*trace" "^\*compilation" "^\*GTAGS" "^\*Help" "^session\.*" "^\*"))
;
;use IDO for more things
(setq recentf-max-saved-items 100)
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
I have confirmed that the logic is somewhat sound by binding the individual commands then running them interactively. It seems as if running the same defs from lisp is acting differently than running the same interactively.
Ido-edit-input is C-e during ido-find-file
(global-set-key [f11] 'delete-minibuffer-contents)
(global-set-key [f12] (lambda () (interactive) (insert "C:/users/family")))
Also tried inserting (sit-for 1) in the mytest defun on the possibility that there is some timing issue.
How can the defun mytest be modified to insert a string (#3) with a single invocation?
This isn't the answer to the question in the title, but it does do what you apparently want:
(defun my-ido-find-file-in-dir ()
(interactive)
(let ((default-directory "C:/users/family"))
(call-interactively 'ido-find-file)))
(global-set-key [f10] 'my-ido-find-file-in-dir)
I'm completely new to both Lisp and Emacs. In Emacs, when coding in Java for example, I want to be able to type "{" then hit "ENTER" and have the next line be ready for whatever is nested in the braces. For example, if I have the following line:
public void method()
and I type "{" then hit return I should get this:
public void method() {
// indentation applied, no additional tabbing necessary
}
I'm already able to insert by pairs, for example, typing "{" gives "{}" with my cursor between the braces. I did this by adding these lines to the emacs init file:
;; insert by pairs (parens, quotes, brackets, braces)
(defun insert-pair (leftChar rightChar)
(if (region-active-p)
(let (
(p1 (region-beginning))
(p2 (region-end))
)
(goto-char p2)
(insert rightChar)
(goto-char p1)
(insert leftChar)
(goto-char (+ p2 2))
)
(progn
(insert leftChar rightChar)
(backward-char 1) ) )
)
(defun insert-pair-brace () (interactive) (insert-pair "{" "}") )
(global-set-key (kbd "{") 'insert-pair-brace)
To get the auto-nesting I described above, I added these lines:
;; automatically nest next line
(defun auto-nest ()
(insert "\n\n")
(backward-char 1)
(insert "\t")
)
(defun auto-nest-brace () (interactive) (auto-nest) )
(global-set-key (kbd "{ RET") 'auto-nest-brace)
When I start up Emacs, however, I get this message:
error: Key sequence { RET starts with non-prefix key {
What am I doing wrong, and what can I do to fix it? I don't want to use a different key combination to do this. There are a lot of text editors in which this auto-nesting is standard, and it should be easy enough to code up in ELisp.
It's great that you are trying to add this functionality to Emacs yourself, but there's no need to reinvent the wheel here. Emacs already has a command for the purpose of auto-indenting; it's called newline-and-indent. It is bound to C-j by default, but you can rebind it to RET
globally:
(global-set-key (kbd "RET") 'newline-and-indent)
for a specific mode only:
(require 'cc-mode)
(define-key java-mode-map (kbd "RET") 'newline-and-indent)
java-mode-map is defined in cc-mode.el and not available by default, that's why you have to require cc-mode before you can modify java-mode-map.
Note that newline-and-indent indents according to major mode. That is, if you're e.g. in java-mode and press RET in some random location that's not meaningful w/r/t Java syntax, it won't insert additional whitespace at the beginning of the new line.
To read all there is to know about newline-and-indent do
C-h f newline-and-indent RET
I have something similar in my emacs config which I have been using for a while. It calls 'newline-and-indent twice then moves the point one line up before indenting correctly. Here is the snippet of code to do this from my config file:
;; auto indent on opening brace
(require 'cc-mode)
(defun av/auto-indent-method ()
"Automatically indent a method by adding two newlines.
Puts point in the middle line as well as indent it by correct amount."
(interactive)
(newline-and-indent)
(newline-and-indent)
(forward-line -1)
(c-indent-line-or-region))
(defun av/auto-indent-method-maybe ()
"Check if point is at a closing brace then auto indent."
(interactive)
(let ((char-at-point (char-after (point))))
(if (char-equal ?} char-at-point)
(av/auto-indent-method)
(newline-and-indent))))
(define-key java-mode-map (kbd "RET") 'av/auto-indent-method-maybe)
Pretty straightforward as you can see. Hopefully it will work for you. I have not used it in any other modes except java.
You want a combination of auto pairs (or alternatives) plus auto indentation. Check out the emacswiki on the former: http://www.emacswiki.org/emacs/AutoPairs
And on the latter:
http://www.emacswiki.org/emacs/AutoIndentation
Suppose I define the following shortcut
(global-set-key (kbd "C-d C-j") "Hello!")
Is it possible to configure emacs so that if I type "C-d C-j C-j C-j" I will get "Hello! Hello! Hello!" rather than having to type "C-d C-j C-d C-j C-d C-j"?
I don’t think you can configure Emacs so that it does that for all commands. However, you can implement this functionality in the commands themselves. This is what is done for C-x e. Here is a macro I just wrote (guided by the standard definition of kmacro-call-macro in GNU Emacs 23.1.1) that makes it easy to add this functionality to your own commands:
(defmacro with-easy-repeat (&rest body)
"Execute BODY and repeat while the user presses the last key."
(declare (indent 0))
`(let* ((repeat-key (and (> (length (this-single-command-keys)) 1)
last-input-event))
(repeat-key-str (format-kbd-macro (vector repeat-key) nil)))
,#body
(while repeat-key
(message "(Type %s to repeat)" repeat-key-str)
(let ((event (read-event)))
(clear-this-command-keys t)
(if (equal event repeat-key)
(progn ,#body
(setq last-input-event nil))
(setq repeat-key nil)
(push last-input-event unread-command-events))))))
Here’s how you use it:
(defun hello-world ()
(interactive)
(with-easy-repeat
(insert "Hello, World!\n")))
(global-set-key (kbd "C-c x y z") 'hello-world)
Now you can type C-c x y z z z to insert Hello, World! three times.
If you are looking for something generic that works on all commands I cant see how that would work - how would emacs know if you are starting a new command or want to repeat the previous. A better example would be "C-c h", if you type "h" after that, should emacs repeat the command or insert a h?
That said, emacs already has a mechanism for this - the universal argument.
Try this key sequence:
C-u 3 C-d C-j
It is even fewer keypresses than C-d C-j C-j C-j C-j
Try something like this:
(global-set-key (kbd "C-c C-j") (lambda()
(interactive)
(insert "Hello!")
(message "Type j to print Hello!")
(while (equal (read-event) ?j)
(insert "Hello!"))
(push last-input-event unread-command-events)))
Idea taken from kmacro-call-macro
No. The sequence "ctrl-d ctrl-j" is what is bound to the string "Hello!" Emacs binds the sequence as a whole to the given string. Here's some good info on the topic:
Link
On the other hand, if you wanted just three instances of "Hello!", you could define that sequence C-d C-j C-d C-j C-d C-j as "Hello! Hello! Hello!", but it would be shorter to just define a simpler sequence for the string you want.
I use this all the time. You'll need library repeat.el (part of GNU Emacs) to use it.
(defun make-repeatable (command)
"Repeat COMMAND."
(let ((repeat-message-function 'ignore))
(setq last-repeatable-command command)
(repeat nil)))
(defun my-hello ()
"Single `hello'."
(interactive)
(insert "HELLO!"))
(defun my-hello-repeat ()
(interactive)
(require 'repeat)
(make-repeatable 'my-hello))
Now bind my-hello-repeat:
(global-set-key (kbd "") 'my-hello-repeat)
Now just hold down the home key to repeat HELLO!.
I use this same technique in multiple libraries, including Bookmark+, thing-cmds, and wide-n.
smartrep is all you want
(require 'smartrep)
(defvar ctl-d-map (make-keymap)) ;; create C-d map
(define-key global-map "\C-d" ctl-d-map)
(smartrep-define-key
global-map "C-d"
'(("C-j" . (insert "hello"))))
I want to make one keystroke, say C-F12, to do delete-other-windows or winner-undo. I think it's easy if I already learning Emacs Lisp programming, and set a boolean flag. That is, if previously it run delete-other-window, now it'll run winner-undo.
How do you do that in Emacs Lisp?
Thanks
Try something like this
(setq c-f12-winner-undo t)
(define-key (current-global-map) [C-f12]
(lambda()
(interactive)
(if c-f12-winner-undo
(winner-undo)
(delete-other-windows))
(setq c-f12-winner-undo (not c-f12-winner-undo))))
(defun swdev-toggle-sole-window ()
(interactive)
(if (cdr (window-list))
(delete-other-windows)
(winner-undo)))
(global-set-key (kbd "<C-f12>") 'swdev-toggle-sole-window)
The first line starts the declaration of a function called swdev-toggle-sole-window, taking no argument.
This function is declared as interactive, i.e. it can be called with M-x or through a key binding.
If the window list contains more than one element, i.e. if there is more than one window, …
… then delete other windows …
… else undo the window deletion.
Bind the function to the key C-f12.
Here's a solution using the approach taken by Emacs' recenter-top-bottom function:
(defun delete-other-window-or-winner-undo ()
"call delete-other-window on first invocation and winner-undo on subsequent invocations"
(interactive)
(if (eq this-command last-command)
(winner-undo)
(delete-other-windows)))
(global-set-key (kbd "<C-f12>") 'delete-other-window-or-winner-undo)