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

binding for <C-tab> doesn't work in god-mode #157

Open
dadinn opened this issue May 23, 2023 · 10 comments
Open

binding for <C-tab> doesn't work in god-mode #157

dadinn opened this issue May 23, 2023 · 10 comments

Comments

@dadinn
Copy link

dadinn commented May 23, 2023

I've tried to configure <C-tab> key-binding to call indent-for-tab-command, while using TAB for company-complete-common-or-cycle, or alternatively company-indent-or-complete-common.

Yet, when in god-mode pressing TAB always calls company's completion.

My expectation is that in god-mode TAB would always indent instead.

@darth10 darth10 added the bug label May 23, 2023
@darth10
Copy link
Collaborator

darth10 commented May 23, 2023

Thanks for reporting this @dadinn!
From a quick check, I can recreate this.
I'll try coming up with a fix soon.

@darth10
Copy link
Collaborator

darth10 commented May 26, 2023

It looks like this is due to God mode getting confused by the many ways key bindings for the TAB can be defined.
For example, you can define a key binding for C-<tab>, <C-tab>, and C-x TAB.
More details here: https://www.emacswiki.org/emacs/TabKey

There needs to be some refactoring done for looking up commands for multiple keys i.e. TAB and <tab>.
I'm not yet sure how that'll shape up, but I feel it's something worth doing despite the niche use case.

@darth10
Copy link
Collaborator

darth10 commented May 26, 2023

Oh, before I forget to mention, a possible workaround for this issue is to define a keybinding for TAB in god-local-mode-map using define-key:

(define-key god-local-mode-map (kbd "TAB") 'indent-for-tab-command)

... or with use-package:

(use-package god-mode
  :bind
  ;; ...
  (:map god-local-mode-map
   ("TAB" . indent-for-tab-command))
  ;; ...
  )

This is slightly roundabout, but it should work.
Let me know if that solves your issue @dadinn.

@dadinn
Copy link
Author

dadinn commented Jun 10, 2023

The "roundabout" solution seem to have worked!

I've also changed the binding for company mode to be ("TAB" . company-complete-common), which achieves my intended behaviour, so that while in god-mode the TAB key functions only as indentation, and while in editing mode only as completion.

@darth10
Copy link
Collaborator

darth10 commented Jun 11, 2023

Thanks for confirming!
I'll add something about the TAB key to the README soon.
Closing for now.

@darth10 darth10 closed this as completed Jun 11, 2023
@dadinn
Copy link
Author

dadinn commented Jun 12, 2023

Uh, oh... I've just realised there seems to be a problem with this solution.

My above configuration prevents TAB to function correctly while in org-mode. (i.e. cycle headlines)

If I take out the TAB binding from the god-mode-local-map, then C-h k shows that god-mode would interpret the TAB key as C-TAB.

It seems to me the problem is that my TAB binding in god-mode-local-map overrides the org-cycle binding in org-mode-map. Adding org-mode to god-exempt-major-modes would be too drastic, as I would like to have the navigation features of god-mode still. Also, this could be a recurring problem with other modes too!

@dadinn
Copy link
Author

dadinn commented Jun 12, 2023

Maybe there has to be an additional mode-map for god-mode, besides god-local-mode-map, which would be lower priority than the key-map of the given major mode? Or some other customization option to adjust the behaviour of god-local-mode-map.

Alternatively, the solution could be to create a utility procedure similar to company-indent-or-complete-common, but for org-mode, something like org-indent-or-cycle, and bind it to C-TAB in org-mode-map. This would still require god-mode to interpret C-TAB correctly, and only use the revert to the plain TAB binding in god-local-mode-map, if C-TAB is not bound in the active major mode.

Not sure, do you think that would work?

@darth10
Copy link
Collaborator

darth10 commented Jun 12, 2023

Maybe there has to be an additional mode-map for god-mode, besides god-local-mode-map, which would be lower priority than the key-map of the given major mode? Or some other customization option to adjust the behaviour of god-local-mode-map.

You can always modify the order of items in minor-mode-alist, but that's a pretty brittle solution.

Alternatively, the solution could be to create a utility procedure similar to company-indent-or-complete-common, but for org-mode, something like org-indent-or-cycle, and bind it to C-TAB in org-mode-map. This would still require god-mode to interpret C-TAB correctly, and only use the revert to the plain TAB binding in god-local-mode-map, if C-TAB is not bound in the active major mode.

Not sure, do you think that would work?

Binding to a custom function is a good approach to having different actions based on certain conditions.
Something like this function can be bound in god-local-mode-map:

(defun org-cycle-or-indent ()
  (interactive)
  (if (eq major-mode 'org-mode)
      (org-cycle)
    (indent-for-tab-command)))

Interpreting the TAB key correctly is problematic in general, but custom functions with bindings in god-local-mode-map are flexible enough to accomodate most situations.

Unfortunately, I simply cannot recreate this in a minimal config with the god-mode and org-mode packages.
TAB always seems to call org-cycle, even with "TAB" binding in god-local-mode-map.
Please let me know if the above function works for you.

@dadinn
Copy link
Author

dadinn commented Jul 8, 2023

Based on your suggestions I came up with the following:

(defvar god-tab-command-alist nil
  "Associatiation list for major-mode specific command to execute for TAB key while in god-mode")
(defun god-tab-command ()
  (interactive)
  (let* ((mode
          (apply
           (function derived-mode-p)
           (mapcar (function car) god-tab-command-alist)))
         (command
          (and mode (alist-get mode god-tab-command-alist))))
    (if command (or (funcall command) (indent-for-tab-command))
      (indent-for-tab-command))))

(bind-key "TAB" . (function god-tab-command))

(add-to-list
 (quote god-tab-command-alist)
 (quote (org-mode . org-cycle)))

It works quite well for me. It allows you to bind different tab-command to execute for different major-modes while god-mode is enabled. Unless that tab-command returns a non-nil value, it will also call indent-for-tab-command. This way the bound function could do some checks and only execute its body when it is relevant in the given context, otherwise return nil/false, which will cause normal indentation to be called instead.

It happens that org-cycle always returns a non-nil value, therefore TAB is never interpreted as indentation, and only org-cycle gets called in org-mode.

Other modes can be added easily by adding an additional pair to god-tab-command-alist.
Also, it is always the function assigned to the most specific match in the major-mode inheritance hierarchy gets called.

I am adding this here just as a reference for anybody who has the same problem. I don't think anything should be changed in god-mode itself. It is fairly straightforward to set this all up manually with simple use-package stanzas.

@darth10
Copy link
Collaborator

darth10 commented Jul 9, 2023

That looks good, @dadinn!
Sorry for the late reply.

This sounds like it might be useful to others so I'll add something to the README with your example.

@darth10 darth10 reopened this Jul 9, 2023
@darth10 darth10 added documentation and removed bug labels Jul 9, 2023
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Development

No branches or pull requests

2 participants