15

EDIT: I understand there is keyboard-quit (which is normally bound to C-g); but I'm more interested to know about how one deals with editing functions that come with Emacs (like in this case). I run into this kind of situations from time to time when I want to change just a little bit of some build-in functions.

In emacs, when you hit M-ESC ESC (or ESC three times), you can get out of a lots of situations like transient-mark, etc. But I habitually hit the escape key (I actually remap this to a single hit of the escape key) more than I intended, and that ends up killing my windows configuration, which is quite annoying. The function keyboard-escape-quit is defined in simple.el:

(defun keyboard-escape-quit ()
  "Exit the current \"mode\" (in a generalized sense of the word).
This command can exit an interactive command such as `query-replace',
can clear out a prefix argument or a region,
can get out of the minibuffer or other recursive edit,
cancel the use of the current buffer (for special-purpose buffers),
or go back to just one window (by deleting all but the selected window)."
  (interactive)
  (cond ((eq last-command 'mode-exited) nil)
    ((> (minibuffer-depth) 0)
     (abort-recursive-edit))
    (current-prefix-arg
     nil)
    ((and transient-mark-mode mark-active)
     (deactivate-mark))
    ((> (recursion-depth) 0)
     (exit-recursive-edit))
    (buffer-quit-function
     (funcall buffer-quit-function))
    ((not (one-window-p t))
     (delete-other-windows))
    ((string-match "^ \\*" (buffer-name (current-buffer)))
     (bury-buffer))))

And I can see that I don't want the lines:

    ((not (one-window-p t))
     (delete-other-windows))

But what is the best way to modify this function? I can see only two ways: 1) modify simple.el 2) copy this function to my .emacs file and do the modifications there. Both ways are not really good; ideally I would like to see something on the line of defadvice, but I can't see how I can do it in this case.

Luke Girvin
  • 13,221
  • 9
  • 64
  • 84
polyglot
  • 9,945
  • 10
  • 49
  • 63

6 Answers6

15

You could use around advice and redefine the offending function to do what you want (i.e. one-window-p should always return t):

(defadvice keyboard-escape-quit (around my-keyboard-escape-quit activate)
  (let (orig-one-window-p)
    (fset 'orig-one-window-p (symbol-function 'one-window-p))
    (fset 'one-window-p (lambda (&optional nomini all-frames) t))
    (unwind-protect
        ad-do-it
      (fset 'one-window-p (symbol-function 'orig-one-window-p)))))

This kind of acts like a (let ...) but has to be more complicated because you need to override a function for a limited scope instead of a variable.

scottfrazer
  • 17,079
  • 4
  • 51
  • 49
  • 4
    A better one-liner version using flet (i.e the function version of let): (defadvice keyboard-escape-quit (around my-keyboard-escape-quit activate) (flet ((one-window-p (&optional nomini all-frames) t)) ad-do-it)) – polyglot Jun 20 '09 at 16:22
  • You might also need to change `flet` to `cl-flet`. – Alan Jan 24 '16 at 20:34
  • @polyglot I know it is 2021 now, but may be you know why Emacs 27.1 says `Warning (bytecomp): ‘(one-window-p (&optional nomini all-frames) t)’ is a malformed function` when I try your code? – user90726 Aug 04 '21 at 14:04
7

I usually find that 'keyboard-quit (C-g) works to get out of all of those situations.

However, if you really want to have a variant of this function, I think that copying to your .emacs file (and renaming, I usually usa a prefix of bp) and making the edits there is probably the best option.

EDIT, in response to edit: In general, whenever I want an edited version of an emacs function, I either write it myself, or copy it to my .emacs, rename it bp-whotever and then do appropriate edits.

The downside of this is that my .emacs is HUGE, and probably extra-crufty with ancient functions that are nolonger used... the upside is that whenever I need to write something new, I've got tons of sample code to look at...

Brian Postow
  • 11,709
  • 17
  • 81
  • 125
5

Here's another, simpler piece of advice that takes advantage of the fact that keyboard-escape-quit calls buffer-quit-function before closing windows:

(defadvice keyboard-escape-quit
  (around keyboard-escape-quit-dont-close-windows activate)
  (let ((buffer-quit-function (lambda () ())))
    ad-do-it))

Works with Emacs 25.1. (I originally used @scottfrazer's advice, but it's unhappy in 25.1. Haven't bothered debugging yet.)

ryan
  • 2,687
  • 1
  • 29
  • 38
1

A single press of the Escape key, by default, acts as a Meta prefix key; that is, a keybinding which involves the Meta key.

Triple-pressing the Escape key will run keyboard-escape-quit, which is like keyboard-quit but with more of a "do what I mean" behaviour.

This code may help with your use case. You can use this in your Emacs init file:

;;; esc always quits
(define-key minibuffer-local-map [escape] 'minibuffer-keyboard-quit)
(define-key minibuffer-local-ns-map [escape] 'minibuffer-keyboard-quit)
(define-key minibuffer-local-completion-map [escape] 'minibuffer-keyboard-quit)
(define-key minibuffer-local-must-match-map [escape] 'minibuffer-keyboard-quit)
(define-key minibuffer-local-isearch-map [escape] 'minibuffer-keyboard-quit)
(global-set-key [escape] 'keyboard-quit)
gsl
  • 1,063
  • 1
  • 16
  • 27
1

I'm more interested to know about how one deals with editing functions that come with Emacs (like in this case). I run into this kind of situations from time to time when I want to change just a little bit of some build-in functions.

This is exactly the purpose for which I created the library el-patch. You would put this in your init-file:

(el-patch-defun keyboard-escape-quit ()
  "Exit the current \"mode\" (in a generalized sense of the word).
This command can exit an interactive command such as `query-replace',
can clear out a prefix argument or a region,
can get out of the minibuffer or other recursive edit,
cancel the use of the current buffer (for special-purpose buffers),
or go back to just one window (by deleting all but the selected window)."
  (interactive)
  (cond ((eq last-command 'mode-exited) nil)
    ((> (minibuffer-depth) 0)
     (abort-recursive-edit))
    (current-prefix-arg
     nil)
    ((and transient-mark-mode mark-active)
     (deactivate-mark))
    ((> (recursion-depth) 0)
     (exit-recursive-edit))
    (buffer-quit-function
     (funcall buffer-quit-function))
    (el-patch-remove
      ((not (one-window-p t))
       (delete-other-windows)))
    ((string-match "^ \\*" (buffer-name (current-buffer)))
     (bury-buffer))))
Resigned June 2023
  • 4,638
  • 3
  • 38
  • 49
0

Here's a new way using cl-lib instead of cl which is now deprecated:

;; Make it so keyboard-escape-quit doesn't delete-other-windows
  (defadvice keyboard-escape-quit
      (around keyboard-escape-quit-dont-delete-other-windows activate)
    (cl-letf (((symbol-function 'delete-other-windows)
               (lambda () nil)))
      ad-do-it))

You'll need to make sure prior to it you have called:

(require 'cl-lib)
Didier A.
  • 4,609
  • 2
  • 43
  • 45