DEV Community

AJ Kerrigan
AJ Kerrigan

Posted on • Updated on

Bring Your Vim/Tmux Navigation Reflexes to VS Code

Introduction

One of the first tmux plugins that changed my brain was Chris Toomey's vim-tmux-navigator. Sure it makes things more convenient, but the core idea is that switching between sections of a terminal window should feel natural and consistent. It shouldn't matter whether those sections are tmux panes or vim splits:

vim-tmux-navigator demo

It's an idea that seems obvious in retrospect. In this post, we'll walk through a couple ways to apply that mindset to VS Code keybindings.

Terminal Muscle Memory in a GUI World

VS Code's customizable keybindings and extensions gallery provide lots of opportunities to make your terminal sensibilities feel at home. Installing the VSCodeVim extension is a great start! But if you feel at home at the terminal, you'll probably be missing a few of your favorite shortcuts.

To keep this post manageable, I'll focus on a few key bindings I use so often in tmux that not having them in VS Code makes my fingers cry:

  • Ctrl-(h|j|k|l): Bounce around between panes
  • Ctrl-a |: Create a vertical split
  • Ctrl-a -: Create a horizontal split
  • Ctrl-a x: Close a pane

Keybindings for Navigation

At a glance, this seems like a simple request. Ctrl plus a vim direction key should take my focus in that direction. So I can add a few key bindings:



    {
        "key": "ctrl+k",
        "command": "workbench.action.navigateUp"
    },
    {
        "key": "ctrl+j",
        "command": "workbench.action.navigateDown"
    },
    {
        "key": "ctrl+h",
        "command": "workbench.action.navigateLeft"
    },
    {
        "key": "ctrl+l",
        "command": "workbench.action.navigateRight"
    }


Enter fullscreen mode Exit fullscreen mode

And... done, right? Except I'm picky, so that doesn't quite cover it. When my focus is in the panel below my open editors, I want Ctrl-(h|l) to move left/right between panel tabs. Fortunately, we can cover that by taking advantage of "when" clause contexts:



    {
        "key": "ctrl+h",
        "command": "workbench.action.previousPanelView",
        "when": "panelFocus"
    },
    {
        "key": "ctrl+l",
        "command": "workbench.action.nextPanelView",
        "when": "panelFocus"
    }


Enter fullscreen mode Exit fullscreen mode

VS Code will use that "when" context to find the most specific matching key binding. So if my focus is in the panel, Ctrl-h will move to the previous panel tab. Otherwise, it will navigate left by one view. Ah... much better, feeling more comfy all the time.

But there's still a tiny thing bugging me. If I move focus into the sidebar when I've got the Explorer viewlet visible, I want to use Ctrl-(j|k) to move between the file explorer and open editors view. If I'm being honest, this isn't even something I really need! I just want it because I noticed it's not there, and it felt like bumping into an invisible wall in a video game.

Again, "when" clause contexts can satisfy even this silly requirement:



    {
        "key": "ctrl+k",
        "command": "workbench.files.action.focusOpenEditorsView",
        "when": "sideBarFocus && explorerViewletVisible"
    },
    {
        "key": "ctrl+j",
        "command": "workbench.files.action.focusFilesExplorer",
        "when": "sideBarFocus && explorerViewletVisible && !filesExplorerFocus"
    }


Enter fullscreen mode Exit fullscreen mode

The "when" clause is a little more complex in this case, but the core concept is the same. Things can get progressively more wild and powerful, combining out-of-the-box contexts with additional contexts from extensions. But that's a story for another day, for the purposes of this post my fingers are happy!

Watch it Work

With these keybindings in place, I can hop around VS Code in a way that maps nicely to my tmux expectations:

VS Code navigation demo

Terminal-lovers, I doubt that your preferences will exactly match mine. Still, I hope that some of the examples in this post will help you tweak VS Code to suit you.

Note: For the record, the codebase I have open there is VisiData. That's not an entirely random choice - if you're happy in the terminal, you'll probably find some handy uses for VisiData. I'll dig into that more in a separate post...

Update (2020-Oct): Here's a separate post :).

In the meantime, thanks for reading! Criticism, comments and questions are all welcome. So fire away in the comments!

References

  • Tmux and Vim - even better together: I'm pretty sure this is the first place I saw the idea to rebind keys for horizontal/vertical splits using | and -. It seemed so obvious in retrospect, I made the change immediately and never looked back.
  • What are your favorite VS Code extensions and why?: There are lots of VS Code extension posts around, it can be overwhelming. Your projects and personal preference will guide this though, so linking to a discussion post seems fitting.
  • Chronicler: I used this VS Code extension to record my session, after a few other methods yielded unreasonably large files and/or messed up colors. Chronicler was less work for better results, nice!

Top comments (7)

Collapse
 
bdannowitzhudl profile image
BDannowitzHudl

I know this post is a bit old, but I put these settings in all my VS Code configurations. One thing that I have an issue with is getting this kind of navigation to work from within .ipynb notebooks.

I've found that this works for left and right navigation:

    {
        "key": "ctrl+h",
        "command": "workbench.action.focusLeftGroup",
        "when": "notebookEditorFocused"
    },
    {
        "key": "ctrl+l",
        "command": "workbench.action.focusRightGroup",
        "when": "notebookEditorFocused"
    }
Enter fullscreen mode Exit fullscreen mode

but there is no such action for focusUpGroup/focusDownGroup, and I can't quite get this work work with navigateUp/navigateDown or panels.

Do you have any immediate insights as to how to accomplish this? Thanks for the great post!

Collapse
 
ajkerrigan profile image
AJ Kerrigan

I don't use notebooks in VS Code much so I didn't have any immediate insights, but your comment got me curious so I went looking for some :D. And the first place I landed was this open issue which you already commented on!

github.com/microsoft/vscode/issues...

I replied to your comment there, but you can add focusAboveGroup and focusBelowGroup bindings to make the workaround cover all directions:

    {
        "key": "ctrl+k ctrl+h",
        "command": "workbench.action.focusLeftGroup",
        "when": "notebookEditorFocused"
    },
    {
        "key": "ctrl+k ctrl+l",
        "command": "workbench.action.focusRightGroup",
        "when": "notebookEditorFocused"
    },
    {
        "key": "ctrl+k ctrl+j",
        "command": "workbench.action.focusBelowGroup",
        "when": "notebookEditorFocused"
    },
    {
        "key": "ctrl+k ctrl+k",
        "command": "workbench.action.focusAboveGroup",
        "when": "notebookEditorFocused"
    }
Enter fullscreen mode Exit fullscreen mode

(At some point after writing this post, I moved my nav bindings into chords off ctrl+k only because I kept running into edge cases where I wanted ctrl+k available.)

Collapse
 
jdsutherland profile image
Jeff Sutherland • Edited

As someone who isn't fully immersed in modern javascript development but is a content user of vim/tmux (including vim-tmux-navigator), I can't imagine there being such a difference as to be worth giving up vim for. I suspect it has something to do with shortcomings with vim language server integration. Is it more than that? What's the motivation for moving to VS Code?

Collapse
 
ajkerrigan profile image
AJ Kerrigan

Hey Jeff, I wouldn't try to convince someone to give up vim for VS Code. I still happily use both myself, I just prefer VS Code more often these days and brought some vim muscle memory along for the ride.

Some of the work I prefer to handle in VS Code (testing and debugging for example) are just personal preference, and someone with a good set of vim skills/plugins/configuration would have no reason to switch. Other bits of functionality (like having side-by-side Markdown previews or Jupyter notebooks in the same editor as your regular code) work more nicely in a graphical environment... but not everybody wants or needs that.

I guess this is a bad sales job - I'd say keep using vim :).

Collapse
 
jdsutherland profile image
Jeff Sutherland

Thanks for the reply AJ.

I'd heard a few people I trust mention either switching or a desire to so it seems like there must be something to this but I haven't probed deeper. I listened to a podcast interview where Chris Toomey himself said as much to my surprise. I've heard that some of the front end tooling is better and IIRC Chris mentioned the language server completion stuff. I just haven't seen what the difference is in practice. If I get more into modern front end development, I'll look further.

Anyways, cheers.

Collapse
 
lxmrc profile image
Alex

Hi AJ, thanks for writing this. I'm looking at switching from vim/tmux to VS Code too. If you could show me how to bind leader + | and leader + - to create vertical and horizontal splits that would be awesome.

Collapse
 
ajkerrigan profile image
AJ Kerrigan • Edited

Hi Alex, that seems like a handy binding :). At first I thought the easiest way to handle that would be inside ~/.vimrc, coupled with the "Use key mappings from .vimrc" setting. That worked for split but not vsplit in my setup. That could be an issue just on my end, I've messed with my settings a bunch since this post!

That said, handling it from the VS Code side worked fine for me. The VSCodeVim extension lets you define mode-level overrides, so a block like this in your settings.json should do the trick:

"vim.normalModeKeyBindings": [
    {
        "before": ["<leader>", "|"],
        "commands": [
            "workbench.action.splitEditorRight"
        ]
    },
    {
        "before": ["<leader>", "-"],
        "commands": [
            "workbench.action.splitEditorDown"
        ]
    }
]
Enter fullscreen mode Exit fullscreen mode