emacs-evil / evil-collection

A set of keybindings for evil-mode
GNU General Public License v3.0
1.22k stars 272 forks source link

Ensure terminal compatibility in various modes. #290

Closed crocket closed 4 years ago

crocket commented 4 years ago

Because TAB is bound to

it is inconvenient to use those modes in terminal emulators. I want TAB to jump between links in those modes.

I worked around the issue with the following configuration.

(use-package evil-collection
  :after evil
  :config
  (evil-collection-init)
  (add-hook 'custom-mode-hook
            (lambda ()
              (define-key evil-normal-state-local-map
                (kbd "TAB") (kbd "<tab>"))))
  (add-hook 'help-mode-hook
            (lambda ()
              (define-key evil-normal-state-local-map
                (kbd "TAB") (kbd "<tab>"))))
  (add-hook 'Info-mode-hook
            (lambda ()
              (define-key evil-normal-state-local-map
                (kbd "TAB") (kbd "<tab>"))
              ;; "r" is bound to Info-history-forward without evil.
              (define-key evil-normal-state-local-map
                (kbd "r") #'Info-history-forward))))
Ambrevar commented 4 years ago

You are right, "" and "TAB" are different. We need to adopt this policy consistently, so we should mention this in the guidelines in the readme as well. Would you like to send a patch? I'd be happy to merge! :)

crocket commented 4 years ago

Do you want to make it a consistent policy to bind TAB to <tab>? I'm still not totally sure about which key Info-history-forward should be bound to.

Ambrevar commented 4 years ago

Policy should be that both TAB and <tab> should be bound for compatibility with terminals. Regarding the difference between C-i and TAB in terminals (e.g. for Info-history-forward) I guess we need to provide a fallback binding. Any suggestion to replace C-i?

yunhao94 commented 4 years ago

I suggest to use the prefixed version g C-i as a fallback of the TAB, since C-o is also remapped in some keymaps, which g C-o can be a fallback as well.

Also, I found that there are some \<M-return> and M-\<return> in evil-collection which can't work under terminal, maybe using M-RET is better?

crocket commented 4 years ago

I created https://github.com/emacs-evil/evil-collection/pull/291 for this issue.

crocket commented 4 years ago

I recommend binding < and > or C-j and C-k to the same functions that <tab> and <S-tab> are bound to. This way, you get redundant bindings that work even on terminal where TAB and C-i are the same. C-i and C-o are vim keys for jumping history.

In modes where C-i is meaningless, binding TAB to the same function that <tab> is bound to should be safe.

I don't really mind binding gh to S-<tab> and gl to <tab>, either.

Thus, we have three sets of candidates.

jojojames commented 4 years ago

Keep in mind I agre with @Ambrevar on making sure the GUI version of Emacs has the best possible set of bindings.

On your suggestions:

< and >

./modes/indium/evil-collection-indium.el:54: ">" 'indium-debugger-next-frame ./modes/realgud/evil-collection-realgud.el:88: ">" 'realgud:cmd-newer-frame ./modes/calendar/evil-collection-calendar.el:58: ">" 'calendar-scroll-left ./modes/image+/evil-collection-image+.el:51: ">" 'imagex-sticky-rotate-right)) ; like sxiv ./modes/geiser/evil-collection-geiser.el:71: ">" 'geiser-doc-next ./modes/calc/evil-collection-calc.el:107: ">" 'calc-scroll-right ./modes/dired/evil-collection-dired.el:148: ">" 'dired-next-dirline ./modes/tablist/evil-collection-tablist.el:49: ">" 'tablist-enlarge-column ./modes/image-dired/evil-collection-image-dired.el:59: ">" 'image-dired-rotate-original-right ; like sxiv ./modes/emms/evil-collection-emms.el:93: ">" 'emms-seek-forward ./modes/emms/evil-collection-emms.el:158: ">" 'emms-seek-forward ./modes/lispy/evil-collection-lispy.el:129: (lispy-define-key map ">" 'lispy-slurp-or-barf-right) ;; `lispy-slurp' ./modes/lispy/evil-collection-lispy.el:288: ">" 'lispy-slurp-or-barf-right ./modes/help/evil-collection-help.el:66: ">" 'help-go-forward

This seems potentially OK given there's no real conflicts.

gh and gl

./modes/mu4e/evil-collection-mu4e.el:166: "gl" mu4e-show-log ./modes/mu4e/evil-collection-mu4e.el:223: "gl" mu4e-show-log ./modes/pdf/evil-collection-pdf.el:163: "gl" 'pdf-view-goto-label ./modes/dired/evil-collection-dired.el:196: "gl" 'dired-subtree-down ./modes/guix/evil-collection-guix.el:98: "gl" 'guix-output-list-lint ./modes/guix/evil-collection-guix.el:113: "gl" 'guix-package-info-lint ./modes/guix/evil-collection-guix.el:186: "gl" 'guix-hydra-build-list-view-log) ./modes/debug/evil-collection-debug.el:59: "gl" 'debugger-list-functions

./modes/neotree/evil-collection-neotree.el:64: "gh" 'neotree-hidden-file-toggle ./modes/w3m/evil-collection-w3m.el:55: "gh" 'w3m-view-header ./modes/calendar/evil-collection-calendar.el:88: "gh" 'calendar-list-holidays ; "gh" in evil-org-agenda. TODO: Shadows calendar-hebrew. ./modes/eww/evil-collection-eww.el:80: "gh" 'eww-list-histories ./modes/alchemist/evil-collection-alchemist.el:75: "gh" 'alchemist-help-history ./modes/dired/evil-collection-dired.el:195: "gh" 'dired-subtree-up

As you can see gh and gl usually have some mneumonic attached to them. I'm not sure if it's appropriate for this case adn the "unchainability" of the keys don't help its case.

C-j and C-k

These already have a general direction to them.

./modes/neotree/evil-collection-neotree.el:68: (kbd "C-j") 'neotree-select-down-node ./modes/xref/evil-collection-xref.el:42: (kbd "C-j") 'xref-next-line ./modes/tar-mode/evil-collection-tar-mode.el:42: (kbd "C-j") 'tar-next-line ./modes/man/evil-collection-man.el:50: (kbd "C-j") 'Man-next-section ./modes/lsp-ui-imenu/evil-collection-lsp-ui-imenu.el:41: (kbd "C-j") 'lsp-ui-imenu--next-kind ./modes/p4/evil-collection-p4.el:64: (kbd "C-j") 'p4-forward-active-link ./modes/p4/evil-collection-p4.el:97: (kbd "C-j") 'p4-goto-next-change ./modes/p4/evil-collection-p4.el:102: (kbd "C-j") 'diff-hunk-next ./modes/p4/evil-collection-p4.el:118: (kbd "C-j") 'p4-next-change-rev-line ./modes/hackernews/evil-collection-hackernews.el:45: (kbd "C-j") 'hackernews-next-item ./modes/popup/evil-collection-popup.el:40: (kbd "C-j") 'popup-next ./modes/cider/evil-collection-cider.el:130: (kbd "C-j") 'cider-repl-history-forward ./modes/cider/evil-collection-cider.el:186: (kbd "C-j") 'cider-stacktrace-next-cause ./modes/cider/evil-collection-cider.el:217: (kbd "C-j") 'cider-inspector-next-page ./modes/vlf/evil-collection-vlf.el:52: (kbd "C-j") 'vlf-next-batch ./modes/gnus/evil-collection-gnus.el:56: (kbd "C-j") 'gnus-summary-next-article ./modes/calendar/evil-collection-calendar.el:64: (kbd "C-j") 'calendar-forward-month ./modes/indium/evil-collection-indium.el:67: (kbd "C-j") 'indium-inspector-next-reference ./modes/indium/evil-collection-indium.el:83: (kbd "C-j") 'indium-debugger-frames-next-frame ./modes/indium/evil-collection-indium.el:106: (kbd "C-j") 'indium-repl-next-input ./modes/occur/evil-collection-occur.el:59: (kbd "C-j") 'occur-next ./modes/eshell/evil-collection-eshell.el:63: (kbd "C-j") 'eshell-next-prompt ./modes/eshell/evil-collection-eshell.el:86: (kbd "C-j") 'eshell-next-prompt ./modes/flycheck/evil-collection-flycheck.el:44: (kbd "C-j") 'flycheck-error-list-next-error ./modes/rtags/evil-collection-rtags.el:78: (kbd "C-j") 'rtags-dependency-tree-next-level ./modes/rtags/evil-collection-rtags.el:107: (kbd "C-j") 'rtags-references-tree-next-level ./modes/pass/evil-collection-pass.el:43: (kbd "C-j") 'pass-next-entry ./modes/slime/evil-collection-slime.el:91: (kbd "C-j") 'sldb-down ./modes/slime/evil-collection-slime.el:139: (kbd "C-j") 'slime-inspector-next ./modes/slime/evil-collection-slime.el:186: (kbd "C-j") 'slime-xref-next-line ./modes/slime/evil-collection-slime.el:197: (kbd "C-j") 'slime-repl-next-prompt ./modes/sly/evil-collection-sly.el:85: (kbd "C-j") 'sly-db-down ./modes/sly/evil-collection-sly.el:130: (kbd "C-j") 'sly-inspector-next ./modes/sly/evil-collection-sly.el:180: (kbd "C-j") 'sly-xref-next-line ./modes/sly/evil-collection-sly.el:193: (kbd "C-j") 'sly-mrepl-next-prompt ./modes/term/evil-collection-term.el:152: (kbd "C-j") 'term-next-prompt ./modes/ert/evil-collection-ert.el:53: (kbd "C-j") 'ert-results-next-test ./modes/elisp-refs/evil-collection-elisp-refs.el:42: (kbd "C-j") 'elisp-refs-next-match ./modes/log-edit/evil-collection-log-edit.el:50: (kbd "C-j") 'log-edit-next-comment ./modes/log-view/evil-collection-log-view.el:59: (kbd "C-j") 'log-view-file-next ./modes/geiser/evil-collection-geiser.el:75: (kbd "C-j") 'forward-button ./modes/geiser/evil-collection-geiser.el:90: (kbd "C-j") 'geiser-repl-next-prompt ./modes/comint/evil-collection-comint.el:44: (kbd "C-j") #'comint-next-prompt ./modes/pdf/evil-collection-pdf.el:107: (kbd "C-j") 'pdf-view-next-page-command ./modes/image/evil-collection-image.el:77: (kbd "C-j") 'image-next-file ./modes/alchemist/evil-collection-alchemist.el:98: (kbd "C-j") 'alchemist-test-next-result ./modes/alchemist/evil-collection-alchemist.el:110: (kbd "C-j") 'alchemist-goto-jump-to-next-def-symbol ./modes/ggtags/evil-collection-ggtags.el:64: (kbd "C-j") 'ggtags-view-search-history-next ./modes/ggtags/evil-collection-ggtags.el:76: (kbd "C-j") 'next-error-no-select ./modes/ggtags/evil-collection-ggtags.el:87: (kbd "C-j") 'previous-error ./modes/deadgrep/evil-collection-deadgrep.el:47: (kbd "C-j") 'deadgrep-forward ./modes/ibuffer/evil-collection-ibuffer.el:134: (kbd "C-j") 'ibuffer-forward-filter-group ./modes/info/evil-collection-info.el:88: ;; We usually use "C-j"/"C-k" for that. ./modes/info/evil-collection-info.el:89: (kbd "C-j") 'Info-forward-node ./modes/doc-view/evil-collection-doc-view.el:41: (kbd "C-j") 'doc-view-next-page ./modes/compile/evil-collection-compile.el:54: (kbd "C-j") 'compilation-next-error ./modes/outline/evil-collection-outline.el:86: (kbd "C-j") 'outline-forward-same-level ./modes/tide/evil-collection-tide.el:48: (kbd "C-j") 'tide-find-next-reference ./modes/tide/evil-collection-tide.el:57: (kbd "C-j") 'tide-find-next-error ./modes/ag/evil-collection-ag.el:48: (kbd "C-j") 'compilation-next-error ./modes/diff-mode/evil-collection-diff-mode.el:99: (kbd "C-j") 'diff-hunk-next ./modes/diff-mode/evil-collection-diff-mode.el:114: (kbd "C-j") 'diff-hunk-next ./modes/emms/evil-collection-emms.el:108: (kbd "C-j") 'emms-browser-next-non-track ./modes/emms/evil-collection-emms.el:159: (kbd "C-j") 'emms-next ./modes/guix/evil-collection-guix.el:203: (kbd "C-j") 'guix-build-log-next-phase ./modes/elfeed/evil-collection-elfeed.el:98: (kbd "C-j") 'elfeed-show-next ./modes/nov/evil-collection-nov.el:46: (kbd "C-j") 'nov-next-document ./modes/prodigy/evil-collection-prodigy.el:72: (kbd "C-j") 'prodigy-next-with-status ./modes/arc-mode/evil-collection-arc-mode.el:43: (kbd "C-j") 'archive-next-line ./modes/youtube-dl/evil-collection-youtube-dl.el:63: (kbd "C-j") 'youtube-dl-list-priority-down ./modes/lispy/evil-collection-lispy.el:209: (lispy-define-key map "C-J" 'lispy-outline-next) ./modes/lispy/evil-collection-lispy.el:266: ;; (define-key map (kbd "C-j") 'lispy-newline-and-indent) ./modes/vc-dir/evil-collection-vc-dir.el:78: (kbd "C-j") 'vc-dir-next-directory ./modes/macrostep/evil-collection-macrostep.el:48: (kbd "C-j") 'macrostep-next-macro ./modes/vc-annotate/evil-collection-vc-annotate.el:52: (kbd "C-j") 'vc-annotate-next-revision ./modes/cus-theme/evil-collection-cus-theme.el:47: (kbd "C-j") 'widget-forward ./modes/cus-theme/evil-collection-cus-theme.el:56: (kbd "C-j") 'widget-forward ./modes/notmuch/evil-collection-notmuch.el:156: (kbd "C-j") 'notmuch-show-next-message ./modes/notmuch/evil-collection-notmuch.el:195: (kbd "C-j") 'notmuch-tree-next-thread ./modes/anaconda-mode/evil-collection-anaconda-mode.el:47: (kbd "C-j") 'next-error-no-select ./modes/reftex/evil-collection-reftex.el:87: (kbd "C-j") 'reftex-select-next-heading ./modes/custom/evil-collection-custom.el:52: (kbd "C-j") 'widget-forward ./modes/monky/evil-collection-monky.el:52: (kbd "C-j") 'monky-goto-next-section ./modes/company/evil-collection-company.el:61: (kbd "C-j") 'company-select-next-or-abort ./modes/company/evil-collection-company.el:73: (kbd "C-j") 'company-select-next-or-abort

And here's the C-o/C-i for completeness sake.

./modes/slime/evil-collection-slime.el:141: (kbd "C-i") 'slime-inspector-next ./modes/slime/evil-collection-slime.el:149: (kbd "C-i") 'slime-inspector-next-inspectable-object ./modes/sly/evil-collection-sly.el:132: (kbd "C-i") 'sly-inspector-next ./modes/sly/evil-collection-sly.el:139: (kbd "C-i") 'forward-button ./modes/sly/evil-collection-sly.el:175: (kbd "C-i") 'forward-button ./modes/sly/evil-collection-sly.el:233: (kbd "C-i") 'forward-button ./modes/info/evil-collection-info.el:56: (kbd "C-i") 'Info-history-forward ./modes/guix/evil-collection-guix.el:74: (kbd "C-i") 'bui-history-forward ./modes/help/evil-collection-help.el:50: (kbd "C-i") 'help-go-forward

./modes/gnus/evil-collection-gnus.el:160: (kbd "C-o") 'gnus-summary-save-article-mail ./modes/dired-sidebar/evil-collection-dired-sidebar.el:47: (kbd "C-o") 'dired-sidebar-find-file-alt ./modes/occur/evil-collection-occur.el:71: (kbd "C-o") 'occur-mode-display-occurrence ./modes/slime/evil-collection-slime.el:137: (kbd "C-o") 'slime-inspector-pop ./modes/sly/evil-collection-sly.el:128: (kbd "C-o") 'sly-inspector-pop ./modes/term/evil-collection-term.el:127: (kbd "C-o") 'term-send-raw ./modes/ibuffer/evil-collection-ibuffer.el:186: (kbd "C-o") 'ibuffer-visit-buffer-other-window-noselect ./modes/info/evil-collection-info.el:52: (kbd "C-o") 'Info-history-back ./modes/guix/evil-collection-guix.el:73: (kbd "C-o") 'bui-history-back ./modes/vterm/evil-collection-vterm.el:92: (kbd "C-o") 'vterm--self-insert ./modes/custom/evil-collection-custom.el:58: (kbd "C-o") 'Custom-goto-parent ./modes/helm/evil-collection-helm.el:178: (kbd "C-o") 'helm-next-source) ./modes/help/evil-collection-help.el:49: (kbd "C-o") 'help-go-back

Maybe a flag to gate the C-i like evil has is appropriate, maybe not.

crocket commented 4 years ago

Different modes can bind different redundant alternatives for <tab> and S-<tab>. I don't really mind.

In help mode, C-j and C-k are vacant. In info mode, < and > are vacant, but you could move Info-forward-node and Info-backward-node to < and > and bind C-j and C-k to <tab> and S-<tab> if you want.

If you want to add evil-collection-C-i-want, you should also make redundant alternative bindings for C-i. So, you are not going to avoid redundancies.

A bit of redundancy is going to be necessary around C-i. Once we take care of C-i, things will be generally fine because RET or C-m is generally bound to the functions <return> is bound to.

jojojames commented 4 years ago

In help mode, C-j and C-k are vacant.

;; "\C-j" 'help-go-forward
;; "\C-k" 'help-go-back

It looks like they can use C-j and C-k here that matches the intention of other modes. Not sure why they're commented. @Ambrevar ?

Do you have a list of modes in mind where 1. C-j/C-k would be unbound and could use TAB/S-TAB functionality?

I'm still of the opinion here that C-j/C-k as a fallback isn't a great idea considering there's already a general "direction" to them (e.g. section/heading browsing).

So, you are not going to avoid redundancies.

Wouldn't it just fall back to TAB?

crocket commented 4 years ago

Wouldn't it just fall back to TAB?

Introducing one more variable adds complexity. When we have space for more bindings, conditionals are not necessary.

Just give C-i to jumping forward. Simple.

jojojames commented 4 years ago

Introducing one more variable adds complexity.

Engineering is always about tradeoffs, a blanket "adds complexity" isn't productive or useful IMO. Either way, the original question about "redundancy" and not "complexity".

When we have space for more bindings, conditionals are not necessary.

The questions are "do we have more space for bindings?", "what are the tradeoffs for using them" and "how do those tradeoffs compare to say, a conditional?"

Just give C-i to jumping forward. Simple.

I use the terminal mode for emacs heavily too so I'm also interested in this topic but I don't think this type of conversation style is productive or helpful. Please avoid in the future.

From @yunhao94

I suggest to use the prefixed version g C-i as a fallback of the TAB, since C-o is also remapped in some keymaps, which g C-o can be a fallback as well.

Seems reasonable, and isn't being used by anything so should be simple to add.

crocket commented 4 years ago

Pressing g C-i and g C-o repetitively instead of Tab and Shift+Tab would be bad for my fingers. Ideally, I want both convenience and consistency. But, I'd rather have the alternatives to <tab> and <backtab> bound on different but convenient places in different modes than have them on consistent but inconvenient places in all modes. Convenience for important keys is more important than enforcing consistency on terminal.

If you want both convenience and consistency for alternatives to <tab> and <backtab> on terminal, I suggest a new guideline.

This is going to displace existing bindings on some modes. However, I think <tab> and <backtab> are important enough to deserve a dedicated space for the alternatives for terminal compatibility.

Ambrevar commented 4 years ago

It looks like they can use C-j and C-k here that matches the intention of other modes. Not sure why they're commented. @Ambrevar ?

I don't remember, sorry. Probably nothing important.

jojojames commented 4 years ago

@crocket Sorry, haven't had a lot of time to do a deep dive of this issue so you'll have to wait some more. :)

As for

Bind C-j and C-k as alternatives to and on terminal.

I don't think it's a good idea, there's a lot of modes using that already and it's not necessarily true tab/backtab is more important than what it would replace.

Pressing g C-i and g C-o repetitively instead of Tab and Shift+Tab would be bad for my fingers.

Have you looked into the key rotation feature @noctuid wrote? I think this might be one scenario where evil-collection should be best efforts about its keybinds but it can't satisfy everybody.

crocket commented 4 years ago

I don't think it's a good idea, there's a lot of modes using that already and it's not necessarily true tab/backtab is more important than what it would replace.

@jojojames I understand that the perception of importance is subjective. But, my another suggestion still stands.

Suboptimal fallback bindings for terminal can be added without sacrificing existing (GUI) bindings.

As I wrote before, I'm okay with having different fallback bindings for <tab> and <backtab> on different modes. On one mode, they could be C-j and C-k. On another mode, they can be < and >. On yet another mode, they can be gh and gl. And so on.

As far as I understand, this suggestion aligns with maintainers' preferences. Custom mode already has C-j and C-k as fallback bindings for <tab> and <backtab>.

Have you looked into the key rotation feature @noctuid wrote?

Is it a way to customize my own bindings? I can do that without key rotation.

crocket commented 4 years ago

Do you have any concrete alternative proposal?

jojojames commented 4 years ago

As far as I understand, this suggestion aligns with maintainers' preferences. Custom mode already has C-j and C-k as fallback bindings for and .

I was mainly responding to this part:

This is going to displace existing bindings on some modes. However, I think and are important enough to deserve a dedicated space for the alternatives for terminal compatibility.

Any change or proposal we have here will take less precendence than any other binding and would lose its binding if another more appropriate binding is found.

But, I'd rather have the alternatives to and bound on different but convenient places in different modes than have them on consistent but inconvenient places in all modes.

I'm speaking from the perspective of one user but I'd prefer more consistency over convenience.

I think consistency can generally be agreed upon but convenience is something more subjective.

For example, I don't find g C-i or g C-o to be too inconvenient but I can see it being so for others.

Convenience for important keys is more important than enforcing consistency on terminal.

I agree this is sometimes the case but I think for evil-collection, enforcing consistency is a better goal IMO (afterall, it's a package meant to increase consistency for evil bindings in emacs packages). I'd rather the scenario, "hmnn pressing g c-o is annoying but I at least know how to hit 'TAB'" vs "which which key is 'TAB again? is it C-j or > or gl?".

Do you have any concrete alternative proposal?

At the moment, I'm most partial to the g C-* style bindings or maybe using > and < as the only best efforts TAB replacement.

If @ambrevar has an idea, we can go with that too.

Ambrevar commented 4 years ago

I don't have any better suggestion, sorry :(

crocket commented 4 years ago

I prefer < and >.

Chaining g C-i and g C-o is substantially harder than chaining C-n and C-p.

Chaining g TABand Shift+Tab might be doable, though. Because Shift+Tab functionality is somehow preserved in terminal emulators and linux virtual terminal. But, it is not as ergonomic as < and >.

Another that comes to my mind is g[ and g] as fallbacks for <tab> and <backtab>.

jojojames commented 4 years ago

Another that comes to my mind is g[ and g] as fallbacks for and .

This seems reasonable to me. I don't think that binding is used at all right now.

crocket commented 4 years ago

I will adjust the pull request to use g[ and g] as fallbacks for <tab> and <backtab>.