Issue overriding the ctrl-tab keybinding


#1

Hey, I’ve recently started using Atom and I’m used to ctrl-tab/ctrl-shift-tab working differently, so I wanted to override the built-in keybinding. After some experimenting, I came up with something similar to what’s suggested in the FAQ (unfortunately, I only discovered the FAQ later).

However, like the FAQ states, it is necessary to unset the ‘ctrl-tab ^ctrl’ and ‘ctrl-shift-tab ^ctrl’ bindings to get this to work correctly. Without unsetting these, the tabbing behavior is seemingly erratic - the ctrl-tab keydown seems to properly tab, but if ctrl is still held down, after a few moments, the built-in binding kicks in and switches to the recent tab.

I have another binding which I use to switch to the recent item, and I would like to maintain similar behavior to the built-in behavior, where the recent item stack is kept unmodified while I tab, and only when releasing the ctrl button the current tab is moved to the top of the stack. It appears I cannot achieve this because, firstly, the pane:show-next-item command immediately puts the item at the top of the stack, and secondly, if I don’t unset the aforementioned rules to handle the ctrl button releasing, everything gets pretty erratic.

Is there no way to achieve my desired behavior?


#2

Is there some complication with unsetting the default keybindings?

If this is the case, then the logic of the stack tracker is getting in your way and the only choice would be to create a package that copies the functionality with slightly different logic. Any command in Atom can be replicated via the API, as far as I know.


#3

I suspected as much. What I’m worried about is that even if I go ahead and implement this with my own package, I’d still have the same issues described above from the secondary binding to move the current item to the top of the stack upon releasing the ctrl key (and this binding would still be necessary for my own package to work). I’m wondering if there’s an actual bug there.


#4

The reason why the keybinding is so complex is specifically to enable the behavior that it sounds like you’re describing as desired as the default.

  1. When you press Ctrl+Tab and do not release Ctrl, Atom activates (displays) the next item in the stack but doesn’t change the order of the stack itself. You can press Ctrl+Tab as many times as you want without updating the order of the stack so long as you don’t release Ctrl.
  2. When you release Ctrl, Atom moves the currently active item to the top of the MRU stack

Are you looking for something different?


#5

What I’m looking for is a combination of that and the “classic” tabbing behavior. Meaning, I want ctrl-tab to switch to the next item (not in the stack, the next ordinal item), but I still want it to maintain the stack order while I’m holding ctrl until it’s released, so that tabbing between several items won’t shuffle the stack until I choose the correct tab.

This allows me to keep the “classic” tabbing order, and have a keystroke to return to the “last” tab.


#6

If I’m understanding what you’re asking for, it sounds like something you’ll have to implement yourself.


#7

Which I perfectly understand. My only concern, as I stated before, is what appears to be buggy behavior that would prevent me from implementing this myself. I would rather not waste my time trying to implement this myself if indeed there’s some underlying bug with how the native handling is implemented which would interfere.

That’s where the first post comes in. Can you explain the seemingly buggy behavior which happens when the ctrl-tab assignment is overridden but the ctrl-tab ^ctrl assignment is not unset?


#8

If you want to override the behavior, you’ll have to override both 1 and 2 in my post. You’ll have to implement your own system of keeping track of which tab is “next” and implement two separate commands to behave the way you expect. Then you override both keybindings with your own commands.


#9

So you’re saying I’ll have to keep my own track of the “recent” stack, even though this is already implemented natively in panes?


#10

Yes, if you want different behavior than what is already implemented natively in the panes.


#11

Sorry to keep pushing on this, but I really think there is an underlying issue here.

Even if I implement my own handling for everything, if I override ctrl-tab ^ctrl to anything other than ‘unset!’ the same errorenous behavior happens. I’ve tested it by binding it to some other random command.

This appears to have to do with the partial key matching in the keymapper, which is apparently timing out and causing the native ctrl-tab binding to be called, which leads to pane:show-next-recently-used-item being triggered unexpectedly. This shouldn’t be happening, it’s a bug with the keymapper, and it doesn’t have to do with the actual implementation details of what I want to achieve.


#12

Can you give exact steps to reproduce the problem and a description of what the bug is in the keybinding resolver? If the bug is in the keybinding resolver (and not in the implementation of tab switching) then you should be able to describe it in terms of behavior that have nothing to do with tab switching.

I’m completely open to there being a bug somewhere. But given what you’ve outlined here, I can’t explain what the bug is to the development team.


#13

Sure, add the following code to your keymap:

'body':
  'ctrl-tab': 'status-bar:toggle'
  'ctrl-tab ^ctrl': 'editor:log-cursor-scope'

These are just two random commands to show the issue. Now, hold down Ctrl and press Tab once, keeping Ctrl held. After a second, your tab will switch to your recent tab. If you have the key binding resolver visible, you can see that the native mapping to pane:show-next-recently-used-item gets called. Releasing Ctrl at this point doesn’t cause the log, but I’m assuming that’s because the key combination already timed-out.

Now, hold down Ctrl and press Tab twice. The second tab causes the status bar to toggle and the tab to switch to the next recent tab, somehow both key bindings get called. In this scenario the time-out never happens, and whenever you release Ctrl the log is shown as expected.

I’m sure this could be reproduced with other unrelated key combinations and commands, nothing about this is specific to tabs. Unfortunately, I can’t point out the exact part of the resolver that’s causing the bug, as I’m not familiar with that code, but the description of the KeymapManager’s multi-keystroke bindings and the timeout causing a “replay” leads me to believe this is where the fault lies.


#14

Would you mind writing this up on https://github.com/atom/atom-keymap?


#15

Done, thanks for your assistance.


#16

Just to update here, in case someone else runs into this issue, it’s already known and has been fixed in the atom-keymap package, it will eventually trickle down to stable release.