unofficial mirror of emacs-devel@gnu.org 
 help / color / mirror / code / Atom feed
From: Robert Weiner <rsw@gnu.org>
To: emacs-devel <emacs-devel@gnu.org>
Subject: Re: Problem quitting properly from transient keymap with one keystroke
Date: Thu, 19 Oct 2017 11:40:36 -0400	[thread overview]
Message-ID: <CA+OMD9iUV=KTuZyJc9Xei7zL0CHTuKem2wdg=v8O4Ho9J81A0g@mail.gmail.com> (raw)
In-Reply-To: <m0wp3r7rtm.rsw@gnu.org>

[-- Attachment #1: Type: text/plain, Size: 4393 bytes --]

On Wed, Oct 18, 2017 at 11:13 PM, Bob Weiner <rsw@gnu.org> wrote:

>
> The problem is that set-transient-map uses pre-command-hook to test the
> stay-in-transient-map function and to exit, so if a keystroke sets the
> flag read by the stay-in-transient-map function to disable the transient
> map, this check is not done until another key is pressed because the
> pre-command-hook is not run again until then (so the code doesn't see
> the flag change until then).
>
> I can't just add a post-command-hook that calls the transient map
> disable function because calling (keyboard-quit) from the
> post-command-hook triggers an error and I imagine that is not a proper
> usage scenario.
>
> Is there any clean way to exit and abort from a transient keymap upon a
> single key press?


​I have figured it out though, the resultant code cries out for a
simplification
in handling ​exiting from a transient map.  The most important part
of the solution is that whatever you add to post-command-hook to process
each key press in the transient map must also manually call the function
that set-transient-map returns to force a quit of the transient map (call
this the quit-function).

The quit-function removes the transient map setup and then calls the on-exit
function (optional 3rd argument to set-transient-map) which does
application-specific
exit processing.  The on-exit function is where the full processing of the
application's {C-g} and quit command have to go.  But even that is not
enough.
The application's post-command-hook must catch {C-g} and continue its
processing;
any attempt to do a (keyboard-quit) from post-command-hook will generate an
error
rather than just doing the quit.  The only thing I found that worked was
using
(top-level) to ensure a clean exit from any recursive-levels when aborting
with
{C-g}.

Here is the resulting abbreviated code for reference.  It seems like a lot
to setup to allow
for aborting and quitting while always ensuring that the quit code is
executed.  The Elisp
manual could use some examples of handling both aborts and quits from
transient maps so this
is clearer.  I guess we could create some macros that would at least make
this setup reusable.

Bob

-----

(defvar hycontrol-frames-mode-map
  (let ((map (make-sparse-keymap)))
    (suppress-keymap map) ;; Disable self-inserting keys.
    (define-key map "\C-g"  (lambda () (interactive) (setq
hycontrol--exit-status 'abort-from-frames)))
    (define-key map "q"     (lambda () (interactive) (setq
hycontrol--exit-status 'quit-from-frames)))))

(defvar hycontrol--quit-function nil
  "Stores function auto-generated by a call to `set-transient-map' to
remove the transient-map later.")

(defconst hycontrol--frames-prompt "Frames> ")

(defun hycontrol-frames-post-command-hook ()
  "Added to `post-command-hook' while in HyControl frames mode."
  (condition-case ()
      (funcall #'hycontrol-frames-post-command-hook-body)
    (quit (funcall #'hycontrol-frames-post-command-hook-body))))

(defun hycontrol-frames-post-command-hook-body ()
  (if hycontrol--exit-status
      (progn (remove-hook 'post-command-hook
'hycontrol-frames-post-command-hook)
     (funcall hycontrol--quit-function))
    (message hycontrol--frames-prompt)))

(defun hycontrol-exit-mode ()
  "Run by the HyControl frame or window transient keymap after it is
disabled."
  (setq inhibit-quit nil)
  (pcase hycontrol--exit-status
    ('abort-from-frames   (progn (ding)
  (setq hycontrol--exit-status nil)
;; Use of (keyboard-quit) here would
;; trigger an error in post-command-hook,
;; so don't use.
(top-level)))
    ('quit-from-frames    (message "Finished controlling frames"))))

(defun hycontrol-stay-in-mode ()
  "Return non-nil if HyControl mode should remain active."
  (null hycontrol--exit-status))

(defun hycontrol-frames ()
  "(Excerpt only) Control frames interactively."
  (interactive "p")
  (setq inhibit-quit t
hycontrol--exit-status nil)
  (funcall #'hycontrol-frames-post-command-hook)
  (add-hook 'post-command-hook 'hycontrol-frames-post-command-hook)
  ;; Use normal event loop with transient-map until {C-g} or {q} is
  ;; pressed, then exit.
  (setq hycontrol--quit-function
(set-transient-map hycontrol-frames-mode-map #'hycontrol-stay-in-mode
   #'hycontrol-exit-mode)))

[-- Attachment #2: Type: text/html, Size: 11256 bytes --]

  reply	other threads:[~2017-10-19 15:40 UTC|newest]

Thread overview: 15+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2017-10-19  3:13 Problem quitting properly from transient keymap with one keystroke Bob Weiner
2017-10-19 15:40 ` Robert Weiner [this message]
2017-10-20 12:24 ` Stefan Monnier
2017-10-20 12:27 ` Stefan Monnier
2017-10-20 14:45   ` Robert Weiner
2017-10-20 15:13     ` Stefan Monnier
2017-10-20 16:04       ` Robert Weiner
2017-10-20 17:34         ` Stefan Monnier
2017-10-20 17:51           ` Robert Weiner
2017-10-20 12:42 ` Oleh Krehel
2017-10-20 14:32   ` Robert Weiner
2017-10-20 14:36   ` Robert Weiner
2017-10-20 14:44   ` Stefan Monnier
2017-10-20 15:26     ` Oleh Krehel
2017-10-20 17:30       ` Stefan Monnier

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

  List information: https://www.gnu.org/software/emacs/

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to='CA+OMD9iUV=KTuZyJc9Xei7zL0CHTuKem2wdg=v8O4Ho9J81A0g@mail.gmail.com' \
    --to=rsw@gnu.org \
    --cc=emacs-devel@gnu.org \
    --cc=rswgnu@gmail.com \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
Code repositories for project(s) associated with this public inbox

	https://git.savannah.gnu.org/cgit/emacs.git

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).