Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Doesn't work with Hydra without modifier keys #17

Open
a13 opened this issue Mar 21, 2020 · 9 comments
Open

Doesn't work with Hydra without modifier keys #17

a13 opened this issue Mar 21, 2020 · 9 comments

Comments

@a13
Copy link
Owner

a13 commented Mar 21, 2020

No description provided.

@a13 a13 changed the title Doesn't work with Hydra Doesn't work with Hydra without modifier keys Mar 21, 2020
@a13
Copy link
Owner Author

a13 commented Mar 21, 2020

doesn't work

(defhydra hydra-zoom (global-map "<f2>")
  "zoom"
  ("g" text-scale-increase "in")
  ("l" text-scale-decrease "out"))

works

(defhydra hydra-zoom (global-map "<f2>")
  "zoom"
  ("C-g" text-scale-increase "in")
  ("C-l" text-scale-decrease "out"))

@a13
Copy link
Owner Author

a13 commented Mar 21, 2020

@jurta sorry for disturbing, as a person who knows about Emacs keymaps internals a /little/ bit more than me, do you have any ideas regarding this issue?

The thing is sometimes (like here or in org-agenda-mode-map) Emacs ignores function-key-map when the single key (without modifier keys) is pressed and runs self-insert-command, while in most cases it works as I expect :)

@jurta
Copy link

jurta commented Mar 21, 2020

Sorry, I'm not an expert in hydra :) maybe hydra author can help to provide the results of processing by hydra internals as plain define-key calls, then I could help to see what is wrong.

In the example above with defhydra, what are the real calls of define-key by hydra?

@a13
Copy link
Owner Author

a13 commented Mar 23, 2020

it doesn't use define-key, but creates a keymap directly

(set
   (defvar hydra-zoom/keymap nil "Keymap for hydra-zoom.")
   '(keymap
     (12 . hydra-zoom/text-scale-decrease)
     (7 . hydra-zoom/text-scale-increase)
     (kp-subtract . hydra--negative-argument)
     (kp-9 . hydra--digit-argument)
     (kp-8 . hydra--digit-argument)
     (kp-7 . hydra--digit-argument)
     (kp-6 . hydra--digit-argument)
     (kp-5 . hydra--digit-argument)
     (kp-4 . hydra--digit-argument)
     (kp-3 . hydra--digit-argument)
     (kp-2 . hydra--digit-argument)
     (kp-1 . hydra--digit-argument)
     (kp-0 . hydra--digit-argument)
     (57 . hydra--digit-argument)
     (56 . hydra--digit-argument)
     (55 . hydra--digit-argument)
     (54 . hydra--digit-argument)
     (53 . hydra--digit-argument)
     (52 . hydra--digit-argument)
     (51 . hydra--digit-argument)
     (50 . hydra--digit-argument)
     (49 . hydra--digit-argument)
     (48 . hydra--digit-argument)
     (45 . hydra--negative-argument)
     (21 . hydra--universal-argument)))

@a13
Copy link
Owner Author

a13 commented Mar 23, 2020

org-agenda bindings (the same issue) use org-defkey wrapper instead, but since org-replace-disputed-keys is nil it should work like define-key

(defun org-key (key)
  "Select key according to `org-replace-disputed-keys' and `org-disputed-keys'.
Or return the original if not disputed."
  (when org-replace-disputed-keys
    (let* ((nkey (key-description key))
       (x (cl-find-if (lambda (x) (equal (key-description (car x)) nkey))
              org-disputed-keys)))
      (setq key (if x (cdr x) key))))
  key)

(defun org-defkey (keymap key def)
  "Define a key, possibly translated, as returned by `org-key'."
  (define-key keymap (org-key key) def))

https://github.com/emacs-mirror/emacs/blob/master/lisp/org/org-agenda.el#L2277-L2416

@a13
Copy link
Owner Author

a13 commented Mar 23, 2020

back to hydra, it works on the first keypress (the bindings are in global-map)

(define-key global-map
  [f2 103]
  'hydra-zoom/text-scale-increase)
(define-key global-map
  [f2 108]
  'hydra-zoom/text-scale-decrease)

But then it sets hydra-zoom/keymap as a transient one using

(internal-push-keymap keymap 'overriding-terminal-local-map)
(defun hydra-zoom/text-scale-increase nil "Call the head `text-scale-increase' in the \"hydra-zoom\" hydra.\n\nThe heads for the associated hydra are:\n\n\"g\":    `text-scale-increase',\n\"l\":    `text-scale-decrease'\n\nThe body can be accessed via `hydra-zoom/body', which is bound to \"<f2>\"."
         (interactive)
         (require 'hydra)
         (hydra-default-pre)
         (let
             ((hydra--ignore t))
           (hydra-keyboard-quit)
           (setq hydra-curr-body-fn 'hydra-zoom/body))
         (condition-case err
             (progn
               (setq this-command 'text-scale-increase)
               (hydra--call-interactively-remap-maybe #'text-scale-increase))
           ((quit error)
            (message
             (error-message-string err))))
         (hydra-show-hint hydra-zoom/hint 'hydra-zoom)
         (hydra-set-transient-map hydra-zoom/keymap
                                  (lambda nil
                                    (hydra-keyboard-quit)
                                    nil)
                                  nil))

@jurta
Copy link

jurta commented Mar 25, 2020

Transient map is a real problem. What you could try to do is either to add around advice on hydra-set-transient-map and add your mappings to its arg keymap, or add after advice and do the same on the modified overriding-terminal-local-map.

@ceed0
Copy link

ceed0 commented Jul 14, 2023

Here is an advice function that does that. Though it uses keymap-set, which is only available starting with emacs 29. Edit2: NOW it works fine with define-key (probably without bugs this time).
Edit3: fixed a recursion

  (defun +translate-keymap (map)
    (let ((tl-map (make-sparse-keymap)))
      (map-keymap
       (lambda (char cmd)
         (when (characterp char)
           (if-let* ((modifiers (event-modifiers char))
                     (event (event-basic-type char))
                     (tl-event (string (reverse-im--translate-char event)))
                     (key (vector `(,@modifiers ,tl-event))))
               (define-key tl-map key cmd)
             (define-key tl-map (string
                                 (reverse-im--translate-char char))
                         cmd))))
       map)
      tl-map))

  (defun +translate-keymap-a (fun map &rest args)
    (if-let* ((map? (keymapp map))
              (comp-map (make-composed-keymap map (+translate-keymap map))))
        (apply fun comp-map args)
      (apply fun map args)))

  (advice-add 'set-transient-map :around #'+translate-keymap-a)
  (advice-add 'hydra-set-transient-map :around #'+translate-keymap-a)

I've tested it a little with hydra and built-in repeat-mode, and it works without problems so far. I can create a pull request if you want.

@ceed0
Copy link

ceed0 commented Jul 15, 2023

By advising use-local-map it works with org agenda and other modes that use it, like mu4e-view, too.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

3 participants